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.

Names fields, Author fields, and @domain

I posted this on Notes.net but I know many people don't visit there anymore, so I'm repeating it here.

I'm having a rather frustrating problem that it appears many other people here have also encountered. I have a Names field that I use to feed an Authors field. The Names field contains canonical names that include @domain at the end. This causes the names not to work in Authors fields.

For example, I'll select my name in a Names field, and it looks like Charles Robinson/DOMAIN on the screen. When I save the document it refreshes and my name gets expanded to Charles Robinson/DOMAIN@DOMAIN. An Author field which is Computed from this Names field gets set to "CN=Charles Robinson/O=DOMAIN@DOMAIN".

Does anyone know what controls whether the domain gets appended, and how to make it stop?

Tuesday, August 14, 2007

what passport backlog?

The third and final international trip of my bone-wearying year of travel is coming up in November. My passport expires this month and I have heard horror stories about it taking forever to get passports renewed so I pulled out all the stops: I sent it off via Priority Mail, paid $60 extra for expedited service, and included a return prepaid Priority Mail envelope. I mailed everything off on Monday, July 30th. The check cleared on August 7th. I received my new passport on Saturday, August 11th. Yesterday, August 13th, I received a letter from the US Passport Administration Office telling me they had received my request and were processing it. :-)

Monday, August 13, 2007

Making sense of Microsoft collaboration

Now that I'm in a Microsoft shop I've been trying to wrap my head around Microsoft's collaboration offering for the past couple of months. Microsoft's marketing says one thing, analysts and experts say something else, and the people I know who actually do collaborative applications with Microsoft tools tell me something entirely different. Then my own research may or may not match up with any or all of the other sources, so I try it out first hand and get a whole different set of results. Yeah, it's been a little frustrating.

During my research I stumbled across an article on Redmondmag.com which really helped. It's from December 2006, but it's still pretty relevant. Here are some interesting bits from the beginning of their discussion.
Microsoft's ambitious collaboration strategy is just beginning to take shape, and it's still confusing. Some products and features are far more ready for prime time than others. IT pros are faced with a portfolio that's voluminous, lacks complete unification and, quite frankly, fails to sidestep a rash of redundancies....

"Being a stack architect is very difficult these days. It used to be so simple to pick the right Microsoft technologies and build a stack," says Tim Huckaby, CEO of InterKnowlogy, a custom .NET development shop. "These days, it's overwhelming."
So what's the scope of the product line we're talking about to implement Microsoft's vision of collaboration? The official list from Microsoft includes:
  • Microsoft Office SharePoint Server 2007
  • Office Communications Server 2007 (formerly Live Communications Server 2005)
  • Exchange Server 2007
  • Office Communicator 2007
  • Office Groove 2007 (formerly Groove Virtual Office 3.1)
  • Office Outlook 2007
Note that those are only what is needed for the official Microsoft collaboration platform. Both of the Sharepoint seminars I have attended made heavy use of Infopath 2007 for online forms, and you would be hard-pressed to work in this environment without the rest of Microsoft Office. Microsoft SQL Server 2005 and Windows Server 2003 with Active Directory are considered infrastructure components and not included in the list. To do any custom development (and you will do a LOT) you need to add Microsoft Visual Studio 2005 to the list.

But do you really need all that? Let's get to the root of what collaboration is. According to Merriam-Webster, "collaborate" means:
  1. to work jointly with others or together especially in an intellectual endeavor
  2. to cooperate with or willingly assist an enemy of one's country and especially an occupying force
  3. to cooperate with an agency or instrumentality with which one is not immediately connected
In the business view of collaboration the first definition fits best (although you may also engage in the second and third :-P ). For collaborative business applications this means we need:
  • A user interface for collecting, displaying and generally working with data and information
  • A place to store the data and information
  • A rules engine for notifying others about changes to the data
  • A delivery mechanism for those messages
Continuing from the Redmonmag.com article:
Herein lies the problem: While Microsoft talks about building a seamless, pervasive collaboration platform, many analysts and users complain that the company has done a poor job of clearly sorting out and positioning the many product pieces that constitute that strategy. They believe there are some pieces that overlap each other in terms of core functions and they don't get an adequate feel for the company's long-term commitment to some components.

...

"They have been dropping the term 'collaborative' for a few years now, but they only talk about it in piecemeal fashion or as part of some point product discussion. [Microsoft] could do a better job helping the market understand what is really a multi-faceted story, and how these different technologies address very different problems," says Dwight Davis, vice president at Ovum Summit Inc., a market researcher in Seattle."
The experts largely agree that deploying the full Microsoft collaboration stack is difficult, confusing and there is overlapping functionality. You can create workflow apps using Infopath 2007, Sharepoint Server 2007, or even ASP.Net. When you look at the punch list of functionality required to do collaboration it quickly becomes apparent that Microsoft's portfolio lacks cohesion, focus and direction.

Okay, so peeling back the layers and getting down to brass tacks, what Microsoft tools do you need to build the same kind of basic collaborative applications that are delivered on Notes and Domino day in and day out? All you need is a form for users to enter data, a view for them to navigate through documents, and some workflow notifications with doc links. It's a surprisingly short list:
  • Visual Studio for creating a user interface
  • IIS to host the ASP.Net application
  • SQL Server to store the data and notify people about the changes
  • Exchange to deliver messages and
  • Outlook to read them
Once you get those basics in place there is still a lot of work to do. A key benefit of using Notes is the use of document links. Microsoft doesn't have a rich client like Notes (yet) so that functionality is much more difficult to achieve unless you go with an ASP.Net web-based solution. For security you can map Active Directory users or groups to SQL Server security at the object level, and you can create roles in SQL Server that encompass groups and individual users. Something like Readers or Authors fields is more difficult to implement, but it is still doable. With all that addressed then there is offline data access to consider and finally you can start thinking about application deployment and updates.

The online colleague who outlined the above strategy to me said that Microsoft doesn't provide a seamlessly integrated solution because that isn't the business they're in. They are in the business of building up integrators and resellers who will do the hard work of creating the seamless solutions. By Microsoft keeping things disjointed and hard to understand it ensures the BP's have business, and also ensures that MS will sell more product. I can't argue with that logic from Microsoft's perpective, and seeing Connections and Quickr on the horizon I can't help but wonder if IBM might be heading more strongly in that direction, too. That's a topic for another post, though. ;-)

In conclusion, if you ignore the hype and the marketing you can develop collaborative solutions on Microsoft technologies without ever touching anything Microsoft lists in their collaboration portfolio. It can be a tough uphill slog, fraught with difficult architectural decisions that can't easily be changed later, but it is possible. If you do decide to go with a pre-built solution framework then Sharepoint is Microsoft's answer. However keep in mind that Sharepoint is more comparable to Quickr and Connections than Domino. Microsoft has no direct answer to Domino or Notes.

Thursday, August 02, 2007

Wall Street Journal: Ten Things Your IT Department Won't Tell You

The Problem: Many companies require that employees get permission from the IT department to download software. But that can be problematic if you're trying to download software that your IT department has blacklisted.

The Trick: There are two easy ways around this: finding Web-based alternatives or bringing in the software on an outside device.

Can you believe something this blasphemous would be published in the Wall Street Journal? My boss shared this article with me, and I was dumbfounded. To their credit they do list potential security issues, but how many people are going to care?

Thanks, WSJ, for making my job even harder.