Friday, September 28, 2007

compare and contrast

Am I the only one who goes a little cross-eyed trying to pick out which Lotus icon is which?


I end up having to click each of the Symphony icons to see what they are since the descriptions get cut off and the icons are so muted and the differences so subtle it's difficult for me to pick them out. At least the descriptions of the Notes 8 icons fit on the screen, but I still have to read them, versus previous releases where the icons were distinct enough I could figure it out more quickly. The icons are just a wash of orange.

Compare this to Office 2003 and Visual Studio 2005.

SNTT - Listing all the IP addresses for a computer

There are times you may want to know whether the current user is connected to your internal network. The really basic way is to just try opening a database. If it fails you know the user isn't connected. :-P But what if something else is going on and the server you try to connect to is down, or you decommission that server or you just want to make the code portable enough so it works in any environment?

The easy way to do this is to retrieve the local IP address. If it's in your range you know the computer is online. Most methods to get the local IP only give you the first one registered. This means if someone is connecting over a VPN the IP address you get won't tell you anything about access to the corporate network. You need to enumerate all the IP interfaces that are connected and figure out if one is connected to the LAN.

Now, how do you go about this? Well, you could go the low tech route and Shell("ipconfig /all > ip.txt") and then parse it. That's certainly doable, but then you have to make sure the user can write to the folder where you put the file, and be sure you clean it up. It gets messy. You could use Windows API calls, such as GetAdaptersInfo, but that doesn't tell you if the connection is actually active, just that an IP address is assigned (think disabled NIC's with static IP's), so then you need to add on a couple more API calls to verify whether each interface is enabled. Bleh.

Enter WMI. Microsoft started shipping Windows Management Instrumentation (WMI) with Windows 2000, and it's also in Windows XP. Alongside WMI is Windows Scripting Host (WSH), which is an object wrapper around a lot of really cool functionality that you had to delve into obscure API calls to get previously. Using WMI and WSH you can easily get all the adapters in a computer, determine if they are active, and know with certainty what the user is connected to.

Function GetIPAddress() As String

Dim nicSet As Variant
Dim strIPAddress As String

'must have error handler enabled, as all
'adapters do not return all information
On Error Resume Next

Set nicSet = GetObject("winmgmts:{impersonationLevel=impersonate}"). _
InstancesOf("Win32_NetworkAdapterConfiguration")

Forall nic In nicSet
If nic.IPEnabled Then
strIPAddress = nic.IPAddress(0)
If Left$(strIPAddress, 3) = "10." Or Left$(strIPAddress, 3) = "192" Then
GetIPAddress = strIPAddress
Exit Forall
End If
End If
End Forall

Set nicSet = Nothing
End Function


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

Thursday, September 20, 2007

sometimes curiosity is a bad thing

Notes 8 isn't officially supported on Windows 2000, but I know from first hand experience that the Basic edition works. That got me to wondering how far back you could go. I got it installed on Windows 95 B (which wasn't easy), but it won't load:


SetProcessAffinityMask is only available in versions of kernel32.dll that started shipping with Windows 2000. On a lark I tried copying that file to Windows 95, but then Windows wouldn't load. It looks like Windows 2000 is as far back as you can go and still run Notes 8 Basic without getting into hacking DLL files.

I knew you were curious about that. :-)

Tuesday, September 18, 2007

Yahoo buying Zimbra

I got the following from Computerworld this morning. The SaaS collaboration space is getting pretty interesting. I'm not sure I buy into this as a viable solution, but there must be something here. Or it's over-hyped and being fed by a media and analyst frenzy. I suppose time will tell.
September 17, 2007 (IDG News Service) -- Yahoo Inc. has agreed to buy Zimbra Inc., a privately held company whose Web-hosted collaboration and messaging suite is considered a successful example of the software-as-a-service model.

Yahoo will acquire Zimbra for $350 million in a transaction expected to close in this year's fourth quarter, the companies said today.

With Zimbra, Yahoo becomes a competitor to Google Inc., Zoho, Cisco Systems Inc.'s WebEx and others that provide Web-hosted collaboration and communication suites.

Zimbra, like other Web-hosted suite providers, is also considered a rival to Microsoft Corp. and its Office suite and Exchange messaging platform.

Yahoo is buying Zimbra in part to boost the Yahoo Mail Web mail service and leverage Zimbra's client base in universities, small and medium-size businesses, and Internet service providers, Yahoo said.

In addition to e-mail, Zimbra's suite also includes calendar and contact management components, as well as an open platform that allows third-party developers to create mini-applications, called Zimlets, for the suite.

After the deal closes, Zimbra will become a Yahoo subsidiary.

Sunday, September 16, 2007

one decision down...

I started my digital camera search back in June, and between then and now I've been doing research and trying out different cameras. I sincerely appreciate the input everyone gave, and I finally decided on the Canon PowerShot S5 IS.


It is an 8 megapixel SLR-like camera with image stabilization and 12x optical zoom. After a whole lot of agonizing I finally decided this style of camera fits my style of photography. I often go from macro to 500mm zoom in a few seconds, and I just can't do that with a SLR. We also travel a lot, and the idea of lugging around extra lenses just so we could take the kind of pictures we've grown accustomed to was very off-putting. The real kicker was when I found it for under $350 from buydig.com, which is half the cost of just the body of most of the SLR's I was looking at. They even included free shipping.

Our friend Pam loaned us her S5 (thanks a billion!) and here are a series of pictures Myron took in our back yard, looking across the marsh. All three pictures were taken with him standing in the same spot. No tripod was used. The first is a regular picture, no zoom at all. (The shadows are from the trees. He is facing slightly northwest and this is early morning.) The second is with the 12x optical zoom. The final is 12x optical with 4x digital zoom (48x digital equivalent). I don't think we'll be using the digital zoom much. Oh, and all those little gray blobs are snails. :-)

Wednesday, September 05, 2007

Notes 8 thoughts

I've been using Notes 8 Standard for doing development for a couple of weeks. Overall it's not been a very positive experience.

Performance

I tested on three computers:
  1. My home PC, which is an Athlon64 X2 5600+, 4GB DDR2-800 RAM, 160GB SATA-II 10,000 RPM hard drive, a nVidia 7600GS video card with 512MB RAM, and Windows XP 64-bit.
  2. My work PC, which is an Athlon64 X2 5000+, 2GB DDR2-533 RAM, 180GB SATA-II 7200 RPM drive, ATI Radeon X1650 512MB video card, and Windows XP 32-bit.
  3. My other work PC, a dual Xeon 2.8Ghz with 3GB DDR2-400 RAM, 64MB nVidia Quadro video, and 72GB SATA-II 7200 RPM hard drive with Windows XP 32-bit.
I turned off the "scan on access" setting in my antivirus client, then did some tests. The startup time after logging into Windows and waiting for everything to load averaged about 45 seconds across all these systems. Once loaded the time to open mail averaged 20 seconds, and often there was no hourglass icon to indicate anything was happening. Opening the Calendar averaged 15 seconds, again usually with no hourglass. Once loaded, switching from Mail to Calendar took approximately 1.5 seconds.

To me that's not acceptable. I shouldn't have to wait for anything on any of those machines, but I can actually watch Notes 8 Standard paint the screen on all three. The client machines I support are mostly in the late P3 and early P4 range, many have 256MB RAM, and nearly all have shared video memory. I didn't even bother testing on any of those.

Missing functionality

A few things got lost in the shuffle of moving Notes from a traditional C or C++ binary to a framework that is based on Eclipse. Here is a short list of the things I know are missing from Notes 8 Standard.
  • Roaming users - not available with Notes 8 Standard, and there are "issues" with roaming users in Notes 8 Basic that prevent you from creating new ones. The solution: don't register roaming users.
  • Citrix / Windows Terminal Services - not supported with Notes 8 Standard. You'll have to wait for 8.01.
  • The Productivity Editors are not integrated at all. You can access them from Notes 8, but I could just as easily add a shortcut to Word. That doesn't make it integrated. Actually, Word is better integrated since I can do Send To > Mail Recipient from Word, but I can't from the Productivity Editors.
I probably missed a few, but those are the biggies that people keep bringing up.

Stuff that changed

There are a number of things that don't work the same but the official response is that this is the new way it works (darkness is the new standard for light?), or it is being considered for a future release.
  • Double-click the Sametime icon in the notification area (system tray) and nothing happens. Right-click and all you get are some status options. Originally they wanted to do away with the icon altogether so I guess this is progress
  • The immediate Inbox refresh no longer works. You have to wait for your refresh interval to kick in and refresh your Inbox.
  • Using a custom image for folders is broken in Notes 8 Standard. Your old image gets skewed and looks warped, and you can't change it from the Standard client. You have to do that from Notes 6, 7, or 8 Basic. A fix is being considered for 8.x.

Upgrade issues

Some settings from Notes 7.x are not preserved.
  • The Universal Toolbar gets hidden during an upgrade from Notes 7 unless it has been modified. The other toolbars don't adjust accordingly so there's just a gaping hole in the toolbar area. So far the response is "this is working as designed".
  • When you show the Universal toolbar it sometimes has repeating icons.
  • The Chat toolbar gets hidden, too.
  • "Automatically add Option Explicit" gets turned off in Domino Designer.
And then the bugs...
  • Toolbars move around on their own.
  • "Open in Client" from Domino Designer doesn't work consistently.
  • Graphics artifacts when dragging dialog boxes around the screen.
  • The Replicator page sometimes pops to the foreground when there is no replication interval set. Once it starts happening it happens on 15 minute intervals, and going to the Replication settings, making a change, clearing it and clicking OK makes it stop. This only happened on one of my test machines.

Usability issues

I find many of the UI elements much too small. The Sidebar selector is so small I miss it more than I hit it, so I just go through the menus if I need it. The icons in the bookmark bar (docked Open bar) are also way too small for me. I find the new preferences dialog extremely confusing, even bewildering. The layout of the preferences is just as foreign (as compared to other Windows applications) as the old one was, but now it's both foreign and different from previous Notes releases.

Conclusion

As far as I'm concerned the Notes 8 Standard Gold release is actually Beta 4. It behaves so randomly that every time you open Notes it's like Christmas morning... or maybe it's more like Bertie Bott's Every Flavor Beans, and sometimes you end up with the vomit flavored one.

anyone else having problems with Netvibes?

I've been having an issue with Netvibes for a while. Arbitrarily it will decide to reset some of my feeds to only show the newest items, and it limits my maximum item count to the number of new items. For example, Ed's comment feed was mysteriously reset to only show 6 items. Now I know there are more than 6 comments on Ed's blog, so something is going wonky. I've gotten the same thing on just about every blog I subscribe to, including my own comment feed, so I know it's not just a specific template or server causing the problem.

I e-mailed Netvibes, but let's just say they've been less than helpful. I know that a lot of you use Netvibes, so could you tell me if you're seeing this too?

Saturday, September 01, 2007

a wedding and a funeral

The wedding - Congratulations to Kelli and Damien


Today we attended the wedding of our neighbor's oldest daughter. This is their first child to get married and it was a huge deal for them. The parents went all out to have an amazing event. The wedding service was presided over by a Catholic priest from Nigeria. He was engaging and funny while still being genuine and respectful. The reception was at the South Carolina Aquarium, and it was a lovely venue for the event. The food was wonderful and the cupcakes were crazy good!

This wedding was particularly interesting to me because it is the first black wedding I have ever attended. The traditions, the flow, the whole vibe and atmosphere was so different from any other wedding I have ever been to. It was a celebration of life in general, and was particularly striking in light of how my weekend started.

The funeral


In January 1995 I left my partner of nearly 3 years and moved in with James* and Steve. Steve and I became very close friends and did pretty much everything together. After living with them for nearly two years I decided to get back into the dating scene. Very soon after that I met Myron and moved to Charleston in May 1997. Over the course of the next several years Steve and I kept in touch, and since we were only 5 hours apart we visited each other fairly regularly. The distance changed the dynamic of the friendship, but it didn't particularly bother me. I'm not a particularly social person and, truthfully, am quite content being alone. I have no problem picking up where I left off with someone a few months or even a few years later.

Through the years the nature of the friendship changed. As I aged I matured, I started therapy, I was learning more about myself. Steve was also doing therapy, changing hobbies, and trying out new things. Then things took a weird turn. Comments were made about me not calling, e-mailing or visiting as often as Steve did. It escalated to Steve saying I must not care as much as he did because I didn't behave how he thought I should. I felt more like a long distance lover than a long distance friend. We had a rather heated exchange about what I perceived to be inappropriate neediness, but Steve thought was just part of being a friend. By this time we had nearly a decade of history together, so I agreed to try to work through the issues. In retrospect I think I was hoping that one of us would come to see the other's point of view more clearly.

I never should have agreed to do that, though. It was incredibly unfair to Steve because I had already checked out. I see now that for me the friendship was dead when it became clear that Steve's definition of friendship carried with it a list of demands I had no intention of meeting. I knew what it would take to be his friend and I thought I could fake my way through it. I carried on the charade for four or five years, and this past Friday after one more "you don't care" outburst I finally told Steve I was through with the pretense.

I can't change the past and I'm not going to beat myself up for doing what I thought was right at the time. I mourned the loss of the friend I had a long time ago. Now it's time to bury the guilt and move on. I'll try to learn from the mistakes.

*Not their real names.

Monday, August 27, 2007

will it leave a stain?

[11:02] <WombatBSD> i need to implement blackberry devices i can remotely detonate
[11:02] <WombatBSD> "ok now hold the blackberry to your head... *clickety*"

Saturday, August 25, 2007

SNTT - the Win32 API series - Getting the UNC path from a mapped drive letter

Someone in the developerWorks Lotus forum asked how to get the UNC path from a mapped drive letter. It turns out it's not exceedingly difficult, but it's not a trivial task, either. The following not only retrieves the path, but ensures it is valid and properly formatted.
Option Public
Option Declare

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Adapted from sample code by Randy Birch, http://vbnet.mvps.org
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Const ERROR_SUCCESS = 0
Const MAX_PATH = 260

Declare Function WNetGetConnection Lib "mpr.dll" Alias "WNetGetConnectionA" _
(Byval lpszLocalName As String, Byval lpszRemoteName As String, cbRemoteName As Long) As Long

Declare Function PathIsNetworkPath Lib "shlwapi.dll" Alias "PathIsNetworkPathA"_
(Byval pszPath As String) As Long

Declare Function PathIsUNC Lib "shlwapi.dll" Alias "PathIsUNCA" _
(Byval pszPath As String) As Long

Declare Function PathStripToRoot Lib "shlwapi.dll" Alias "PathStripToRootA" _
(Byval pPath As String) As Long

Declare Function PathSkipRoot Lib "shlwapi.dll" Alias "PathSkipRootA" _
(Byval pPath As String) As Long

Declare Function lstrcpyA Lib "kernel32" (Byval RetVal As String, Byval Ptr As Long) As Long
Declare Function lstrlenA Lib "kernel32" (Byval Ptr As Any) As Long

Sub Initialize
Msgbox GetUncFullPathFromMappedDrive("Z:")
End Sub

Function GetUncFullPathFromMappedDrive(sLocalName As String) As String

Dim sLocalRoot As String
Dim sRemoteName As String
Dim sRemotePath As String
Dim cbRemoteName As Long

sRemoteName = Space$(MAX_PATH)
cbRemoteName = Len(sRemoteName)

sLocalRoot = StripPathToRoot(sLocalName)

'modification to the GetUncFromMappedDrive()
'routine. Save the path info to a variable for
're-adding below.
sRemotePath = StripRootFromPath(sLocalName)

If PathIsNetworkPath(sLocalRoot) = 1 Then
If WNetGetConnection(sLocalRoot, _
sRemoteName, _
cbRemoteName) = ERROR_SUCCESS Then

sRemoteName = QualifyPath(TrimNull(sRemoteName)) & sRemotePath

If IsUNCPathValid(sRemoteName) Then
GetUncFullPathFromMappedDrive = sRemoteName
End If

End If
End If
End Function

Private Function StripPathToRoot(Byval spath As String) As String

'Removes all of the path except for
'the root information (ie drive. Also
'removes any trailing slash.
Dim pos As Integer

Call PathStripToRoot(spath)

pos = Instr(spath, Chr$(0))
If pos Then
StripPathToRoot = Left$(spath, pos - 2)
Else
StripPathToRoot = spath
End If

End Function

Private Function StripRootFromPath(Byval spath As String) As String

'Parses a path, ignoring the drive
'letter or UNC server/share path parts
'You need to use a separate function
'because the API call modifies the value
'you pass to it.
StripRootFromPath = TrimNull(GetStrFromPtrA(PathSkipRoot(spath)))

End Function

Private Function QualifyPath(spath As String) As String

'add trailing slash if required
If Right$(spath, 1) <> "\" Then
QualifyPath = spath & "\"
Else
QualifyPath = spath
End If

End Function

Private Function IsUNCPathValid(Byval spath As String) As Boolean

'Determines if string is a valid UNC
IsUNCPathValid = PathIsUNC(spath) = 1

End Function

Private Function GetStrFromPtrA(Byval lpszA As Long) As String

'Given a pointer to a string, return the string
GetStrFromPtrA = String$(lstrlenA(Byval lpszA), 0)
Call lstrcpyA(Byval GetStrFromPtrA, Byval lpszA)

End Function

Private Function TrimNull(startstr As String) As String
Dim lFound As Long

lFound = Instr(1, startstr, Chr$(0))
If lFound > 0 Then
TrimNull = Left$(startstr, lFound - 1)
Else
TrimNull = startstr
End If

End Function

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

Imitation is the sincerest form of flattery

The Notes 8 Home page includes some cool button icons. I'm reworking some of my own apps (more on that shortly) and wanted to include a launcher page that has the Notes 8 look and feel. I trust Mary Beth Raven's team did some extensive studies of what worked on the Home page, so (without getting too cliche) why reinvent the wheel and recreate everything from scratch?

Start with a blank canvas


The icons on the Notes 8 Home page look like this:





In order to create your own it's easiest to wipe out the default icon. This proved easier said than done for me because the button background is a gradient. Yes, it's extremely subtle, but trust me, it's a gradient. I use Fireworks for my very rudimentary graphics work and couldn't figure out how to select an area and fill it with a gradient, so I went through and overlaid it pixel by pixel. Yeah it was tedious, but here's the final result:





Yay, a blank canvas!

Build a better button


Finding images that work was a little challenging. The icon in the center is 32 x 32 pixels. Through lots of trial and error I determined that you can go as large as 40 x 40 pixels, but after that it starts crowding the frame and looking constrained. A while back I stumbled across some really cool icons from Rokey.net that I've been using for some internal apps at work, so I thought I'd use some of those here. It is important that you pick something that has transparency, and that will scale down to at least 40 pixels (32 pixels is ideal) without getting too ugly.

Here is what I picked (from the wifun PNG library)









Cute, huh? There is also a 32 x 32 version, and when I put it on the button here is how it ended up.





Tip: Each button is 48 pixels wide, including the borders. To put a 32 x 32 image in the center of the blue button it should be positioned at 8, 8 (X, Y). For the orange button it will be 57, 8. And here's another tip: after you position the image on the left, do a copy and paste of it. In Fireworks this puts a copy of it on top of the original, so then all you have to do is move it horizontally using the arrow keys. This is easier than trying to drag it around and position it. Since I already did the calculations for the offsets for you, you could just as easily type in the coordinates, too.

Conclusion


When you're working on updating applications don't feel like you have to continually reinvent the wheel. Lotus misses the mark on some UI queues, but for graphics and iconography they do pretty well. And don't dismiss the usability studies and other work that went into the Notes 8 UI.

Keeping things consistent for users increases their rate of adoption and lowers the barrier to entry. And hey, reusing other people's work just makes it easier for you. :-) If you can, have fun with it even if it's just for the early releases and demos. Who knows, maybe the stuffed shirt who designs the TPS reports might actually like to see a screaming box of chocolates as an icon.

Universal toolbar mangled in Notes 7 > 8 upgrade

Notes 7.0.2 > Notes 8 Basic. This was my development machine.





Notes 7.0.3 > Notes 8 Standard. This was a friend's development machine. Different setup, different domain, different state, even. :-)

Friday, August 24, 2007

How to programmatically create an Access ADE or MDE file

My primary job is working with a massive Access Database Project (ADP). Before this is distributed to end users it gets compiled to an ADE. We have a utility that handles the deployment, but I still had to create the ADE file manually... and I often forgot, which meant I had to sometimes publish the same update multiple times. It's convoluted, I'll go into detail about it some other time.

Anyway, I finally decided it was time to automate this portion of the process so I couldn't forget it. It took a while, but here are the results. Note that this code is from VB6, but it should work equally well from any VB/VBA type environment:
Sub GenerateMDEFile(SourcePath As String, DestPath)
'SourcePath is the ADP file. DestPath is the ADE file.

On Error Resume Next
'I don't normally do this, but with automation you sometimes get weird errors
' that will cause the calling app to crash, but they can be safely ignored.
' Also, since we're using SendKeys you don't want an error dialog to pop up
' and start getting your keystrokes. That would be bad.

Dim objAccess As Variant
Set objAccess = CreateObject("Access.Application")

'Delete the destination (ADE) file if it exists
If Dir$(DestPath) <> "" Then
Kill DestPath
End If

'Make sure the Access window is active
objAccess.Visible = True
DoEvents

'This pastes in the source path, then hits Enter twice.
' The first Enter accepts the source path
' The second accepts the default name for the compiled version
SendKeys SourcePath & "{Enter}{Enter}"
objAccess.DoCmd.RunCommand acCmdMakeMDEFile 'Constant with a value of 7

objAccess.Quit
Set objAccess = Nothing
End Sub


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

Sources: Xtreme Visual Basic Talk, Microsoft Knowledgebase

Wednesday, August 22, 2007

blog cleanup

I finally added a tag cloud so I'll be going through and cleaning up my tags. I think this will likely cause a lot of my posts to reappear as new, and I apologize for that in advance.

Tuesday, August 21, 2007

Crazy cool technology - IBM Live Partition Mobility (updated 8/22/2007)

From the IBM Press room:
In June, IBM shipped its first System p 570 servers with the POWER6 processor -- the world's fastest chip -- containing a unique design that creates dozens to hundreds of "virtual" servers on a single box. Live Partition Mobility, currently in beta testing with general availability planned later this year, is a continuous availability feature that will enable POWER6-based servers, such as the System p 570, to move live logical partitions -- including the entire operating system and all its running applications -- from one server to another while the systems are running.
Update 8/22/2007: How's that for disruptive innovation? I was reminded that VMWare has offered VMotion for a long time. Live Partition Mobility is cool, but not quite as bleeding edge as I first thought.

ISO OOXML approval still highly contentious

Santhosh D'Souza, a Sun employee, has an interesting write-up of the lack of standards in voting for standards. His observations range from the Netherlands' contingent almost unanimously deciding to vote No, but since the local Microsoft contingent wanted to vote Yes the Netherlands instead went with an Abstain vote, to he unbelievable situation in Switzerland (emphasis mine):
The chair-person of the sub-committee deciding on the Swiss vote ruled that objections raised by participants were not enough to justify a Disapprove with comments option, presenting the body with only Approve with Comments or Abstain with Comments choices. The Swiss Internet User Group has outlined its objections to OOXML as an ISO standard, added that the majority wanted to vote Disapprove with Comments and alleges conflict of interest in the choice of chair-person (nominated by the committee chairman who is the Secretary General of ECMA).
Overall an extremely disheartening read. I'm beginning to think OOXML will actually get approved, but it's only through shady deals and underhanded practices. I'm both surprised and not surprised.

Monday, August 20, 2007

Notes 8 Contacts don't work on first try

I installed Notes 8 Standard on my PC today, clicked Contacts and was greeted with this:

I click OK, the dialog reappears, I click OK again, and then I get to this:


I checked the Notes 8 forum and found someone with the same problem in Beta 3. The solution: close and reopen Notes. For those who are interested, I never had Notes or Eclipse installed on this PC previously, so this was about as clean an install as you could possibly get. I'm sincerely hoping this was a one-time glitch, I'll be deploying Notes 8 Standard for the CxO's and their admin assistants at a client soon. :-\

Sunday, August 19, 2007

Happy Birthday

... to me!

Today is my 34th birthday, and one of the more memorable. Last night Myron made reservations for both dinner and a room at Woodlands Resort & Inn, in Summerville, SC. Woodlands is one of the most award-winning inns in the US, and the dining room is similarly lauded. It is one of three properties in the US to achieve Mobil 5 stars in both dining and lodging, and the only property in South Carolina to receive the AAA 5 Diamond Culinary Award.

I won't turn this into a review of Woodlands, but we had two stand out experiences I have to share. First was the Shooting Star Blue Franc 2005, which was served with the chef's tasting menu. It was paired with a truffled mac and cheese, itself a luscious an extravagant dish, and the wine matched it perfectly. The bouquet was intoxicating, the taste was soft, and the finish assertive but not explosive. This is arguably one of the best wines I have ever had and I'm going to have to find some, price be damned.

Second was the flight of Grand Marnier I had with dessert. I have known for a while that there are a few different iterations of Grand Marnier. The most common is called Cordon Rouge, which is the red label. There is also a 50 year and 150 year version, named for the vintage of the cognac that goes into the liqueur. If you like Grand Marnier and you have a chance to try these other versions, do it! The only bad thing is now I'll have a hard time going back to the lesser quality after having tried the higher end varieties. *sigh* If only all my problems were so easily solved.

Wednesday, August 15, 2007

SNTT - the Win32 API series - Finding an open window by caption

I already posted how you can find an application by its class name or if you know the full text of the titlebar. But what if you don't know the class name, or the text in the titlebar changes (such as Domino Designer)? Here is a way to search on partial matches in the titlebar.
(Declarations)
Declare Function GetForegroundWindow Lib "user32" () As Long
Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (Byval hwnd As Long, Byval lpString As String, Byval cch As Long) As Long
Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (Byval hwnd As Long) As Long
Declare Function GetWindow Lib "user32" (Byval hwnd As Long, Byval wCmd As Long) As Long

Const GW_HWNDNEXT = 2

Function API_GetHandleFromPartialCaption(sCaption As String) As Long
Dim lhWnd As Long
Dim sStr As String
API_GetHandleFromPartialCaption = False
'start with the current application
lhWnd = GetForegroundWindow
Do While lhWnd <> 0
sStr = String$(GetWindowTextLength(lhWnd) + 1, Chr$(0)) 'set the buffer to the length of the and fill it with null characters
GetWindowText lhWnd, sStr, Len(sStr) 'get the window title
sStr = Left$(sStr, Len(sStr) - 1) 'strip off the null chars
If Instr(1, sStr, sCaption) > 0 Then 'found it!
API_GetHandleFromPartialCaption = lhWnd
Exit Do
End If
'try again
lhWnd = GetWindow(lhWnd, GW_HWNDNEXT)
Loop
End Function
To use this, call API_GetHandleFromPartialCaption and you'll get back the hWnd of that application, or 0 if it's not found. What can you do with that? Well, you could close the application:
(Declarations)
Declare Function SendMessage Lib "user32" Alias "SendMessageA" (Byval hwnd As Long, Byval wMsg As Long, Byval wParam As Long, lParam As Long) As Long
Const WM_CLOSE = &H10

Function CloseDesigner
Dim hWnd As Long

hWnd = API_GetHandleFromPartialCaption("IBM Lotus Domino Designer")
If hWnd > 0 Then
Call SendMessage(hWnd, WM_CLOSE, 0, 0)
End If
End Function
Or bring it to the foreground...
(Declarations)
Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long

Function SwitchToDesigner
Dim hWnd As Long

hWnd = API_GetHandleFromPartialCaption("IBM Lotus Domino Designer")
If hWnd > 0 Then
Call SetForegroundWindow(hWnd)
End If
End Function
(Note: this particular example could also be achieved with @LaunchApp("Designer").

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