Saturday, July 22, 2006

SNTT - the Win32 API series - FindWindow and LotusScript

FindWindow is a Windows API call that gives you the handle to a specific window. For example, you can use FindWindow to get the instance of Excel then bring it to the front, resize it, close it, or any number of other things. A window handle is a good thing to be able to get.

The typical declare statement for FindWindow looks like this:

Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

The first parameter is the class of the window you want to get. For Notes it's just "Notes", but for Microsoft applications things get a little crazy. If you don't care which instance of an application you get you can just pass a placeholder for the parameter you don't care about. For example, to get the Notes window from VB6 or prior you would call it like this:

hWnd = FindWindow("Notes", vbNullString)

vbNullString is a constant that evaluates to "", so I thought I could simply use the following from LotusScript:

hWnd = FindWindow("Notes", "")

That doesn't work. After extensive searching, and help from friends in the Efnet IRC channel C#, I learned that the VB runtime is actually passing 0 when you use vbNullString. There's a lot going on with marshalling and LPSTR and other stuff I don't fully understand, but the upshot is that to get this working from Notes you have to change the declaration:

Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As Long) As Long

And then you can do:

hWnd = FindWindow("Notes", 0)

Okay, so if you know the class that's great, but finding the class can be a bit of a pain. What if you do know the exact window title? Then you have to declare it with the class as Long and the window name as String:

Declare Function FindWindowByClass Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As Long) As Long
Declare Function FindWindowByTitle Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Long, ByVal lpWindowName As String) As Long

Crazy, I know. I'm not sure if the fault is LotusScript's inability to pass pointers or Microsoft's API implementation. But this should help anyone trying to use FindWindow from LotusScript.

Migrating from iSeries to Red Hat - Part 1

Moving Domino from to a new hardware and OS platform takes careful planning and lots of testing. I have been working on this project for over a year and wanted to share the lessons I have learned along the way.

Currently I run five Domino partitions on an iSeries 270 Dedicated for Domino. Yes, that's one of the bumblebee boxes. I'm migrating to an IBM BladeCenter with a DS4300 SAN. We have two blades for Domino, with mail services on one and applications on another. I am debating installing a secondary Domino partition on each blade and using them for clustering, but that's another set of posts.

The first step in this process was to determine the best approach for moving things over. After extensive research and lots of testing with VMWare Workstation I decided on the following scenario:
  1. Install the same version of Domino on RHEL that I have on the iSeries. In my case this is 6.5.4 FP3.
  2. Create a folder in /local/ for each Domino partition
  3. Create an IFS share on the iSeries for the root Domino folder (all my Domino partitions are installed in /NOTES/ on the iSeries)
  4. Mount the share on linux and copy the files from the iSeries to linux
  5. Adjust notes.ini to use the new path structure
Many people move Domino to new hardware in conjunction with an upgrade to a new version. Don't install a new version of Domino on the new hardware and try to copy everything over. That is difficult to do correctly unless you really know what you're doing. Using the two-step approach gives you the opportunity to create a known working installation before you do the upgrade to a new version.

That's the 50,000 foot view of where I'm headed. In upcoming posts I will cover installing RHEL and configuring it for Domino, installing Domino, configuring ODBCDirect database drivers, migrating servers, and tweaking applications so they work correctly. I expect to learn a lot throughout this process.

Thursday, July 20, 2006

SNTT - How to shell a Windows application from LotusScript and close it

A recent post on Notes.Net asked about shelling a Windows application and closing it. I suggested the poster look up a couple of Windows API calls that would help, then I went digging for them myself. It wasn't quite as simple as I expected to find the necessary information, so I thought I would post it here:
(Declarations)
Const SEE_MASK_NOCLOSEPROCESS = &H40
Const SEE_MASK_FLAG_NO_UI = &H400

Private Type SHELLEXECUTEINFO
cbSize As Long
fMask As Long
hwnd As Long
lpVerb As String
lpFile As String
lpParameters As String
lpDirectory As String
nShow As Long
hInstApp As Long
lpIDList As Long
lpClass As String
hkeyClass As Long
dwHotKey As Long
hIcon As Long
hProcess As Long
End Type

Declare Function ShellExecuteEx Lib "shell32.dll"_
Alias "ShellExecuteEx" (SEI As SHELLEXECUTEINFO) As Long

Declare Function TerminateProcess Lib "kernel32"_
Alias "TerminateProcess" (Byval hProcess As Long, Byval uExitCode As Long) As Long

Sub Initialize
Dim SEI As SHELLEXECUTEINFO

SEI.cbSize = Len(SEI)
SEI.fMask = SEE_MASK_NOCLOSEPROCESS Or SEE_MASK_FLAG_NO_UI
SEI.lpVerb = "open"
SEI.lpFile = "c:\sample.txt"
SEI.nShow = 1
SEI.hInstApp = 0
SEI.lpIDList = 0

Call ShellExecuteEx(SEI)

Messagebox "Application opened.", 0, "Success"
Call TerminateProcess(SEI.hProcess, 0)

End Sub

This LotusScript was converted to HTML using the ls2html routine,
provided by Julian Robichaux at nsftools.com.


,

Tuesday, July 18, 2006

My love/hate relationship with Domino

I have been very critical about Lotus Notes, Domino and IBM in general. Some people have taken that to mean I don't like any of the above. This posting is to set the record straight and demonstrate that is emphatically not the case.

My background is pretty broad, ranging from PBX administrator to DBA to developer. As a developer I have worked with mostly BASIC-like languages: MS Access VBA, Visual Basic, and LotusScript. I have always been intensely interested in data. How it gets collected, how to manipulate it, and how to turn it into useful information. This love affair was awakened in my first programming job, which was using MS Access 2.0.

When I tried to transition from MS Access to VB I quickly grew bored of writing data access classes. MS Access had all that built in and was easy to work with, but in VB I was rolling my own code every stinking time. (I refused to use data-bound controls in VB4/5/6.) So I ended up in a job generically titled "PC Specialist". I was "the IT guy" -- if it had a plug on it, I was responsible for it. This is where I was exposed to a vast array of technologies, responsibilities, and systems. Part of this included making the decision between Exchange and Domino in 1999.

Management wanted to use Domino because our primary supplier did. I didn't know much about Domino or Exchange, but a few things turned me off to Exchange from the start. First, I had serious concerns about my (in)ability to properly secure Windows servers and maintain a high level of security. Second, Exchange required a sprawl of servers and services to function properly and I was intimidated by the prospect of trying to keep all the balls in the air just to get e-mail to flow. Third, programming applications meant using VB and SQL Server and going back to the old hell of writing data access code. Notes development couldn't possibly suck more than that.

And it didn't suck, it was simply amazing. Notes had most of what I loved about MS Access, that being rapid application development tightly bound to the underlying datastore, while being server-based and including granular security. I spent several years gleefully plumbing the depths of what Notes could do, getting my R5 PCLP along the way.

As my applications became more complex I started hitting walls. Debugging Lotus Connectors was difficult and frustrating, and the documentation was atrocious. New features in Domino, such as policies and remote debugging, simply did not work. Clustering Domino servers across multiple platforms required lots of workarounds that you only found by trial and error. I grew to loathe Domino Designer and it's bizarre property window, code stuffed everywhere and the half-baked classes implementation.

So off I went to find the new new thing, something that offered everything Domino had without all the annoying bits. In my quest I tried out MS Access 2003, C#, VB.Net, J2EE, and Ruby on Rails. Broadening the scope I worked with LAMP, OpenACS, Zope/Plone, and Puakma. Most recently I tried out SharePoint Portal 2007 and Exchange 2007. I never found anything that could reach the bar set by Notes and Domino.

Nothing else offered the ease of rich client development, granular security, and an integrated single-server install. There are still annoying parts about working with Notes and Domino, but in the final analysis they are just annoying. The fact remains that you cannot replace Domino with anything else without a tremendous amount of effort. I've climbed that mountain and found there is no summit. The only viable integrated collaboration platform available is Domino.

For all it's knobby warts, I truly do love Notes and Domino. I see in Hannover an opportunity for IBM to finally get it right. The criticisms I voice stem from an intense desire to silence the naysayers.

Wednesday, July 12, 2006

Domino migration - iSeries to Red Hat

We currently run Domino 6.5.4 FP3 on an iSeries 270. Several months ago we purchased an IBM BladeCenter with the idea of migrating the Domino servers to that. I have done lots of testing and I'm finally starting that process. I intend to blog about all the steps I take to get there, focusing heavily on the "gotchas" and things that I have to really dig to find.

Here's an overview of what I'll be doing:
  • Moving Domino server named MAIL to its own blade
  • Moving Domino servers named Intranet, Armstrong, and TranSouth to the same blade
  • Use Internet Sites documents to service two websites from this second blade
  • Use ODBC to connect to an iSeries DB2 server (not the same one currently running Domino)
On the surface it sounds relatively simple. In testing it has been a bit more difficult to get everything talking to each other. I'll be doing the MAIL move on Saturday, July 22nd. If that goes well I'll start the Intranet move, too.

Friday, July 07, 2006

what's the cubert thing about?


I've been asked by a few people why I use the name cubert. It has nothing to do with the 80's video game. My partner and I have a Pembroke Welsh Corgi. When we were researching names we decided to give her a real Welsh name. We came up with several, but many were nearly unpronounceable by us. We finally came up with Beti, which is the Welsh spelling of the English name Betty and pronounced the same way. Here is Beti as a puppy.

But that doesn't explain why I took the nickname cubert. While doing this research for Beti I stumbled across the name Cubert. In Welsh mythology he was the son of Daenre, a succubus, and an unnamed hero. I like mythology, I liked the name, so I took this as my own. :-)

Thursday, July 06, 2006

LotusScript Performance Tip - Clearing the object cache

A while back I was browsing SearchDomino looking for an answer to a problem and stumbled across a posting by Andre Guirard that made a reference to using the Delete statement to remove NotesDocument objects from memory. I was intrigued, so I asked on Notes.Net and Andre responded.

The relevant code is in Andre's response, but in the interest of completeness I will repeat it here:


Dim session As New NotesSession
Dim coll As NotesDocumentCollection
Dim db As NotesDatabase
Dim docCur As NotesDocument, docNext As NotesDocument

Set db = session.CurrentDatabase
Set coll = db.UnprocessedDocuments
Set docCur = coll.GetFirstDocument

Do Until docCur Is Nothing
Call session.UpdateProcessedDoc(docCur)
Set docNext = coll.GetNextDocument(docCur)
' INSERT CODE HERE TO PROCESS A SINGLE DOCUMENT
Delete docCur ' so documents don't accumulate in cache
Set docCur = docNext
Loop


What I'm not clear on, and have not been able to test yet, is whether this also applies to Forall loops and not just GetFirst/GetNext constructs. I also don't typically use NotesViewEntryCollections, so I don't know if it applies to those, either.