Monday, November 21, 2011

On SQL Server and romance

Summary: Quick comment on SQL Server and PostgreSQL.
Quote of the day comes from Rob Sullivan:
"The SQL Server install is born from a truck stop romance between TFS and Sharepoint that someone found in a garbage bag in a dumpster and burned to a DVD."
Ha-ha... so true.

On a separate note, Rob's post raised my interest in PostgreSQL. I wonder, what challenges PostgreSQL presents to user, admins and developers (especially to those coming from the SQL Server camp).

Thursday, November 3, 2011

Windows batch scripting simplified

Summary: A few tips for Windows batch script developers.
One of my recent projects called me to write a couple of batch scripts (something I haven't done in a while). This post is mostly a reference for myself, but I hope it helps some unfortunate soul stuck in the last century's technology.

If you find yourself working on a batch script, chances are you will need to implement command-line parsing. You can obviously use simple position-based parameters (e.g. %1, %2, etc), but if you need anything more sophisticated, you may find the _mtplib.bat library more rewarding.

"What is the the _mtplib.bat library?" you may ask. It's a batch file implementing a few general-purpose functions (such as command-line parsing), which you can call from your batch script. [In case you wonder: yes, your batch script can call code from another batch script.] The _mtplib.bat library [mtp stands for MacMillan Technical Publishing] was originally released by Tim Hill, the author of Windows NT® Shell Scripting, a book I cannot overpraise.

Unfortunately, the link to the source code in the book does not work. After extensive search, I managed to find a version of _mtplib.bat at CodeProject, but some of the characters in the source code appear to be corrupted (I tried them in different browsers and using different character sets to no avail).

I cleaned up the file and made a couple of minor changes (e.g. added a helper function to imitate a delay). I also, cleaned up another sample published by Tim Hill: skeleton.bat (an outline that can be used to create a batch script). I made one change to the skeleton file: all of the calls to the procedures implemented in _mtplib.bat from skeleton.bat invoke them via full path (instead of just the name of the file). This change assumes that the library is located in the same folder (this way you do not need to put _mtplib.bat in your path or working directory when you execute your scripts). And I extended skeleton.bat to a more illustrative sample.bat which shows how to parse command line, call procedures, handle errors, etc. You can download the source code from: You will find the listings of the source code at the end of this post, but before, let me recommend a few excellent resources for Windows batch script developers: And now, the source code (if you encounter any issues, please let me know):

_mtplib.bat
@echo OFF

@if not "%ECHO%"=="" echo %ECHO%
@if not "%OS%"=="Windows_NT" goto DOSEXIT

rem $Workfile: _mtplib.bat $ 
rem $Revision: 2 $ 
rem $Date: 12/04/97 9:51a $
rem $Archive: .../_mtplib.bat $
rem UPDATED BY ALEK DAVIS on 11/02/2011

rem If no arguments, show version information and exit
if "%1"=="" (
 (echo Script MTP Script Library [%0] $Revision: 2 $)
 (goto :EOF)
)

rem At least one argument, so dispatch to procedure
set _PROC=%1
shift /1
goto %_PROC%

rem ------------------------------------------------------------------
rem INIT procedure
rem Must be called in local state before other procs are used
rem
:INIT
if defined TRACE %TRACE% [proc %0 %*]

goto :EOF

rem ------------------------------------------------------------------
rem VARDEL procedure
rem Delete multiple variables by prefix
rem
rem Arguments:
rem %1=variable name prefix
rem
:VARDEL
if defined TRACE %TRACE% [proc %0 %*]
 for /f "tokens=1 delims==" %%I in ('set %1 2^>nul') do set %%I=
goto :EOF

rem ------------------------------------------------------------------
rem PARSECMDLINE procedure
rem Parse a command line into switches and args
rem
rem Arguments:
rem CMDLINE=command text to parse
rem %1=0 for new parse (def) or 1 to append to existing
rem
rem Returns:
rem CMDARG_n=arguments, CMDSW_n=switches
rem CMDARGCOUNT=arg count, CMDSWCOUNT=switch count
rem RET=total number of args processed
rem
:PARSECMDLINE
if defined TRACE %TRACE% [proc %0 %*]
 if not {%1}=={1} (
  (call :VARDEL CMDARG_)
  (call :VARDEL CMDSW_)
  (set /a CMDARGCOUNT=0)
  (set /a CMDSWCOUNT=0)
 )
 set /a RET=0
 call :PARSECMDLINE1 %CMDLINE%
 set _MTPLIB_T1=
goto :EOF

:PARSECMDLINE1
if {%1}=={} goto :EOF
set _MTPLIB_T1=%1
set _MTPLIB_T1=%_MTPLIB_T1:"=%
set /a RET+=1
shift /1
if "%_MTPLIB_T1:~0,1%"=="/" goto :PARSECMDLINESW
if "%_MTPLIB_T1:~0,1%"=="-" goto :PARSECMDLINESW
set /a CMDARGCOUNT+=1
set CMDARG_%CMDARGCOUNT%=%_MTPLIB_T1%
goto :PARSECMDLINE1

:PARSECMDLINESW
set /a CMDSWCOUNT+=1
set CMDSW_%CMDSWCOUNT%=%_MTPLIB_T1%
goto :PARSECMDLINE1
goto :EOF

rem ------------------------------------------------------------------
rem GETARG procedure
rem Get a parsed argument by index
rem
rem Arguments:
rem %1=argument index (1st arg has index 1)
rem
rem Returns:
rem RET=argument text or empty if no argument
rem
:GETARG
if defined TRACE %TRACE% [proc %0 %*]
 set RET=
 if %1 GTR %CMDARGCOUNT% goto :EOF
 if %1 EQU 0 goto :EOF
 if not defined CMDARG_%1 goto :EOF
 set RET=%%CMDARG_%1%%
 call :RESOLVE
goto :EOF

rem ------------------------------------------------------------------
rem GETSWITCH procedure
rem Get a switch argument by index
rem
rem Arguments:
rem %1=switch index (1st switch has index 1)
rem
rem Returns:
rem RET=switch text or empty if none
rem RETV=switch value (after colon char) or empty
rem
:GETSWITCH
if defined TRACE %TRACE% [proc %0 %*]
 (set RET=) & (set RETV=)
 if %1 GTR %CMDSWCOUNT% goto :EOF
 if %1 EQU 0 goto :EOF
 if not defined CMDSW_%1 goto :EOF
 set RET=%%CMDSW_%1%%
 call :RESOLVE
 for /f "tokens=1* delims=:" %%I in ("%RET%") do (set RET=%%I) & (set RETV=%%J)
goto :EOF

rem ------------------------------------------------------------------
rem FINDSWITCH procedure
rem Finds the index of the named switch
rem
rem Arguments:
rem %1=switch name
rem %2=search start index (def: 1)
rem
rem Returns:
rem RET=index (0 if not found)
rem RETV=switch value (text after colon)
rem
:FINDSWITCH
if defined TRACE %TRACE% [proc %0 %*]
 if {%2}=={} (set /a _MTPLIB_T4=1) else (set /a _MTPLIB_T4=%2)

 :FINDSWITCHLOOP
 call :GETSWITCH %_MTPLIB_T4%
 if "%RET%"=="" (set RET=0) & (goto :FINDSWITCHEND)
 if /i "%RET%"=="%1" (set RET=%_MTPLIB_T4%) & (goto :FINDSWITCHEND)
 set /a _MTPLIB_T4+=1
 goto :FINDSWITCHLOOP

 :FINDSWITCHEND
 set _MTPLIB_T4=
goto :EOF

rem ------------------------------------------------------------------
rem REGSETM and REGSETU procedures
rem Set registry values from variables
rem
rem Arguments:
rem %1=reg context (usually script name)
rem %2=variable to save (or prefix to save set of vars)
rem
:REGSETM
if defined TRACE %TRACE% [proc %0 %*]
 for /f "tokens=1* delims==" %%I in ("set %2 2^>nul") do call :REGSET1 HKLM %1 %%I "%%J"
goto :EOF

:REGSETU
if defined TRACE %TRACE% [proc %0 %*]
 for /f "tokens=1* delims==" %%I in ("set %2 2^>nul") do call :REGSET1 HKCU %1 %%I "%%J"
goto :EOF

:REGSET1
set _MTPLIB_T10=%4
set _MTPLIB_T10=%_MTPLIB_T10:\=\\%
reg add %1\Software\MTPScriptContexts\%2\%3=%_MTPLIB_T10% >nul
reg update %1\Software\MTPScriptContexts\%2\%3=%_MTPLIB_T10% >nul
goto :EOF

rem ------------------------------------------------------------------
rem REGGETM and REGGETU procedures
rem Get registry value or values to variables
rem
rem Arguments:
rem %1=reg context (usually script name)
rem %2=variable to restore (def: restore entire context)
rem
rem Returns:
rem RET=value of last variable loaded
rem
rem WARNING: 
rem The "delims" value in the FOR commands below is a TAB  
rem character, followed by a space. If this file is edited by
rem an editor which converts tabs to spaces, this procedure
rem will break!!!
rem
:REGGETM
if defined TRACE %TRACE% [proc %0 %*]
 for /f "delims=     tokens=2*" %%I in ("reg query HKLM\Software\MTPScriptContexts\%1\%2 ^|find "REG_SZ"") do call :REGGETM1 %%I "%%J"
goto :EOF

:REGGETU
if defined TRACE %TRACE% [proc %0 %*]
 for /f "delims=     tokens=2*" %%I in ("reg query HKCU\Software\MTPScriptContexts\%1\%2 ^|find "REG_SZ"") do call :REGGETM1 %%I "%%J"
goto :EOF

:REGGETM1
set _MTPLIB_T10=%2
set _MTPLIB_T10=%_MTPLIB_T10:\\=\%
set _MTPLIB_T10=%_MTPLIB_T10:"=%
set %1=%_MTPLIB_T10%
set RET=%_MTPLIB_T10%
goto :EOF

rem ------------------------------------------------------------------
rem REGDELM and REGDELU procedures
rem Delete registry values
rem
rem Arguments:
rem %1=reg context (usually script name)
rem %2=variable to delete (def: delete entire context)
rem
:REGDELM
if defined TRACE %TRACE% [proc %0 %*]
 call :GETTEMPNAME
 echo y >%RET%
 reg delete HKLM\Software\MTPScriptContexts\%1\%2 <%RET% >nul
 del %RET%
goto :EOF

:REGDELU
if defined TRACE %TRACE% [proc %0 %*]
 call :GETTEMPNAME
 echo y >%RET%
 reg delete HKCU\Software\MTPScriptContexts\%1\%2 <%RET% >nul
 del %RET%
goto :EOF


rem ------------------------------------------------------------------
rem SRAND procedure
rem Seed the random number generator
rem
rem Arguments:
rem %1=new seed value
rem
:SRAND
if defined TRACE %TRACE% [proc %0 %*]
 set /a _MTPLIB_NEXTRAND=%1
goto :EOF

rem ------------------------------------------------------------------
rem RAND procedure
rem Get next random number (0 to 32767)
rem
rem Returns:
rem RET=next random number
rem
:RAND
if defined TRACE %TRACE% [proc %0 %*]
 if not defined _MTPLIB_NEXTRAND set /a _MTPLIB_NEXTRAND=1
 set /a _MTPLIB_NEXTRAND=_MTPLIB_NEXTRAND * 214013 + 2531011
 set /a RET=_MTPLIB_NEXTRAND ^>^> 16 ^& 0x7FFF
goto :EOF

rem ------------------------------------------------------------------
rem RESOLVE procedure
rem Fully resolve all indirect variable references in RET variable
rem
rem Arguments:
rem RET=value to resolve
rem
rem Returns:
rem RET=as passed in, with references resolved
rem
:RESOLVE
if defined TRACE %TRACE% [proc %0 %*]
 :RESOLVELOOP
 if "%RET%"=="" goto :EOF
 set RET1=%RET%
 for /f "tokens=*" %%I in ('echo %RET%') do set RET=%%I
 if not "%RET%"=="%RET1%" goto :RESOLVELOOP
goto :EOF

rem ------------------------------------------------------------------
rem GETINPUTLINE procedure
rem Get a single line of keyboard input
rem
rem Returns:
rem RET=Entered line
rem
:GETINPUTLINE
if defined TRACE %TRACE% [proc %0 %*]
 call :GETTEMPNAME
 set _MTPLIB_T1=%RET%
 copy con "%_MTPLIB_T1%" >nul
 for /f "tokens=*" %%I in ("type '%_MTPLIB_T1%'") do set RET=%%I
 if exist "%_MTPLIB_T1%" del "%_MTPLIB_T1%"
 set _MTPLIB_T1=
goto :EOF

rem ------------------------------------------------------------------
rem GETSYNCFILE procedure
rem Get a sync file name (file will not exist)
rem
rem Returns:
rem RET=Name of sync file to use
rem
:GETSYNCFILE
if defined TRACE %TRACE% [proc %0 %*]
 call :GETTEMPNAME
goto :EOF

rem ------------------------------------------------------------------
rem SETSYNCFILE procedure
rem Flag sync event (creates the file)
rem
rem Arguments:
rem %1=sync filename to flag
rem
:SETSYNCFILE
if defined TRACE %TRACE% [proc %0 %*]
 echo . >%1
goto :EOF

rem ------------------------------------------------------------------
rem DELSYNCFILE procedure
rem Delete sync file
rem
rem Arguments: 
rem %1=sync filename
rem
:DELSYNCFILE
if defined TRACE %TRACE% [proc %0 %*]
 if exist %1 del %1
goto :EOF

rem ------------------------------------------------------------------
rem WAITSYNCFILE procedure
rem Wait for sync file to flag
rem
rem Arguments:
rem %1=sync filename
rem %2=timeout in seconds (def: 60)
rem
rem Returns:    
rem RET=Timeout remaining, or 0 if timeout
rem
:WAITSYNCFILE
if defined TRACE %TRACE% [proc %0 %*]
 if {%2}=={} (set /a RET=60) else (set /a RET=%2)
 if exist %1 goto :EOF
 
 :WAITSYNCFILELOOP
 sleep 1
 set /a RET-=1
 if %RET% GTR 0 if not exist %1 goto :WAITSYNCFILELOOP
goto :EOF

rem ------------------------------------------------------------------
rem GETTEMPNAME procedure
rem Create a temporary file name
rem
rem Returns:
rem RET=Temporary file name
rem
:GETTEMPNAME
if defined TRACE %TRACE% [proc %0 %*]
 if not defined _MTPLIB_NEXTTEMP set /a _MTPLIB_NEXTTEMP=1
 if defined TEMP (
  (set RET=%TEMP%)
 ) else if defined TMP (
  (set RET=%TMP%)
 ) else (set RET=%SystemRoot%)
 
 :GETTEMPNAMELOOP
 set /a _MTPLIB_NEXTTEMP=_MTPLIB_NEXTTEMP * 214013 + 2531011
 set /a _MTPLIB_T1=_MTPLIB_NEXTTEMP ^>^> 16 ^& 0x7FFF
 set RET=%RET%\~SH%_MTPLIB_T1%.tmp
 if exist "%RET%" goto :GETTEMPNAMELOOP
 set _MTPLIB_T1=
goto :EOF

rem ------------------------------------------------------------------
rem DELAY procedure
rem Sleep for the specified number of seconds
rem
rem PARAMETERS
rem %1=Number of seconds to sleep
:DELAY
if defined TRACE %TRACE% [proc %0 %*]
 if {%1}=={} goto :EOF
 @ping 127.0.0.1 -n 2 -w 1000 >nul
 @ping 127.0.0.1 -n %1% -w 1000 >nul
goto :EOF

rem These must be the FINAL LINES in the script
:DOSEXIT
echo This script requires Windows NT
rem ------------------------------------------------------------------

skeleton.bat
@echo OFF

@if not "%ECHO%"=="" echo %ECHO%
@if not "%OS%"=="Windows_NT" goto DOSEXIT

rem $Workfile: skeleton.bat $ 
rem $Revision: 2 $ 
rem $Date: 12/04/97 9:51a $
rem $Archive: .../skeleton.bat $
rem UPDATED BY ALEK DAVIS

rem Set local scope and call MAIN procedure
setlocal & pushd & set RET=
set SCRIPTNAME=%~n0
set SCRIPTPATH=%~f0
set SCRIPTDIR=%~dp0

if "%DEBUG%=="1" (set TRACE=echo) else (set TRACE=rem)
call %SCRIPTDIR%_mtplib :INIT %SCRIPTPATH%
if /i {%1}=={/help} (call :HELP %2) & (goto :HELPEXIT)
if /i {%1}=={/?} (call :HELP %2) & (goto :HELPEXIT)
if /i {%1}=={/h} (call :HELP %2) & (goto :HELPEXIT)
call :MAIN %*

:HELPEXIT
popd & endlocal & set RET=%RET%
goto :EOF

rem ------------------------------------------------------------------
rem HELP procedure
rem Display usage information
rem
:HELP
if defined TRACE %TRACE% [proc %0 %*]
rem Put help message here
goto :EOF

rem ------------------------------------------------------------------
rem MAIN procedure
rem 
:MAIN
if defined TRACE %TRACE% [proc %0 %*]
rem Put main script code here
goto :EOF

rem ------------------------------------------------------------------
rem Additional procedure go here

rem These must be the FINAL LINES in the script
:DOSEXIT
echo This script requires Windows NT
rem ------------------------------------------------------------------

sample.bat
@echo OFF

rem Sample batch script
rem Requires _mtplib.bat to be in the same folder.

@if not "%ECHO%"=="" echo %ECHO%
@if not "%OS%"=="Windows_NT" goto DOSEXIT

rem Set local scope and call MAIN procedure
setlocal & pushd & set RET=
set SCRIPTNAME=%~n0
set SCRIPTPATH=%~f0
set SCRIPTDIR=%~dp0
set SCRIPTEXT=%~x0

if "%DEBUG%"=="1" (set TRACE=echo) else (set TRACE=rem)
call %SCRIPTDIR%_mtplib :INIT %SCRIPTPATH%
if /i {%1}=={/help} (call :HELP %2) & (goto :HELPEXIT)
if /i {%1}=={/?} (call :HELP %2) & (goto :HELPEXIT)
if /i {%1}=={/h} (call :HELP %2) & (goto :HELPEXIT)
call :MAIN %*

:HELPEXIT
popd & endlocal & set RET=%RET%
goto :EOF

rem ------------------------------------------------------------------
rem HELP procedure
rem Display usage information
rem
rem ------------------------------------------------------------------
rem HELP procedure
rem Display usage information
rem
:HELP
if defined TRACE %TRACE% [proc %0 %*]
echo DESCRIPTION
echo.
echo Does this or that on the specified server.
echo.
echo SYNTAX
echo.
echo %ScriptName% [this^|that] [switches]
echo.
echo ARGUMENTS
echo.
echo this
echo    Does this.
echo.
echo that
echo    Does that.
echo.
echo /s:server
echo.
echo    Name of server affected by this or that.
echo    If not specified, local server will be used.
echo.
echo /u:user
echo.
echo    Name of the user performing this or that.
echo    If not specified, active Windows user will be used.
echo.
echo /p:password
echo.
echo    Password of the the user identified by switch /u.
echo.
echo EXAMPLES
echo.
echo    %ScriptName% this /s:XYZ /u:BillieJean /p:IsNotMyLover
echo.
echo        Does this on server XYZ as user BillieJean.
echo.
echo    %ScriptName% that /s:XYZ
echo.
echo        Does that on server XYZ as active Windows user.
goto :EOF

rem ------------------------------------------------------------------
rem MAIN procedure
rem 
:MAIN
if defined TRACE %TRACE% [proc %0 %*]
rem If no arguments, display help
if /i {%1}=={/h} (call :HELP %2) & (goto :HELPEXIT)
if /i {%1}=={} (call :HELP %2) & (goto :HELPEXIT)

rem Initialize variables
set SERVER=
set USER=
set PASSWORD=

rem Process command line and set up variables
set CMDLINE=%*
call %SCRIPTDIR%_mtplib :PARSECMDLINE 0

rem Process positional arguments
if %CMDARGCOUNT% LSS 1 (call :HELP) & (goto :EOF)
call %SCRIPTDIR%_mtplib :GETARG 1
set OPERATION=%RET%
if /i "%OPERATION%"=="this" set OPERATION=this
if /i "%OPERATION%"=="that" set OPERATION=that

rem Process command-line switches
set /a IX=1
:GETSWITCHLOOP
 call %SCRIPTDIR%_mtplib :GETSWITCH %IX%
 if "%RET%"=="" goto :GETSWITCHLOOPEND
 set /a IX+=1
 if /i "%RET%"=="/s" set SERVER=%RETV%
 if /i "%RET%"=="/u" set USER=%RETV%
 if /i "%RET%"=="/p" set PASSWORD=%RETV%
goto :GETSWITCHLOOP
:GETSWITCHLOOPEND

rem Use defaults for non-specified variables
if "%SERVER%"=="" set SERVER=%COMPUTERNAME%
if "%USER%"=="" set USER=%USERDOMAIN%\%USERNAME%

rem Check the operation type and perform this or that
if /i "%OPERATION%"=="this" (
 call :DOTHIS
) else if "%OPERATION%"=="that" (
    call :DOTHAT
) else (
    goto :HELP
)
goto :EOF

rem ------------------------------------------------------------------
rem DOTHIS procedure
rem Print parameter values and sleep for 5 seconds.
rem
:DOTHIS
if defined TRACE %TRACE% [proc %0 %*]
 echo Started this on %DATE% at %TIME%

 call :PRINTPARAMS

 echo Please wait 5 seconds...
 call %SCRIPTDIR%_mtplib :DELAY 5
 if %ERRORLEVEL% neq 0 goto :EOF

 echo Ended this on %DATE% at %TIME%
goto :EOF

rem ------------------------------------------------------------------
rem DOTHAT procedure
rem Print parameter values and sleep for 10 seconds.
rem
:DOTHAT
if defined TRACE %TRACE% [proc %0 %*]
 echo Started that on %DATE% at %TIME%

 call :PRINTPARAMS
 
 echo Please wait 10 seconds...
 call %SCRIPTDIR%_mtplib :DELAY 10
 if %ERRORLEVEL% neq 0 goto :EOF

 echo Ended that on %DATE% at %TIME%
goto :EOF

:PRINTPARAMS
if defined TRACE %TRACE% [proc %0 %*]
 echo Server  =%SERVER%
 echo User    =%USER%
 echo Password=%PASSWORD%
goto :EOF

rem These must be the FINAL LINES in the script
:DOSEXIT
echo This script requires Windows NT
rem ------------------------------------------------------------------

See also:
DOS - Script Snippets (library of various DOS snippets)
Using parameter qualifiers in Windows shell scripts

Tuesday, September 27, 2011

Technobrief #13

Summary: Recent findings of software, articles, and more.
CSS Graphics Hardware JavaScript
  • Create Bookmarklets – The Right Way
    This tutorial looks into creating bookmarklets from scratch and lists some best practices to follow.
  • Superexpert JavaScript Reference
    Great reference with examples and browser support indicators.
  • Namespacing in JavaScript
    Angus Croll offers several approaches of implementing namespaces in JavaScript.
  • Ten Oddities And Secrets About JavaScript
    This article is aimed at intermediate developers who are curious about more advanced JavaScript. It is a collection of JavaScript’s oddities and well-kept secrets. Some sections will hopefully give you insight into how these curiosities can be useful to your code, while other sections are pure WTF material.
jQuery Libraries
  • Simple Data
    A light-weight, dynamic data access component for C# 4.0.
  • .NET Logging Framework
    The logging framework provides you with an easy means to log information to any destination. It comes complete with loggers that support writing to the system console, a file on disk, a TCP/IP socket, and more.
Office Opinions
  • And Then Along Comes Larry….
    Robert Cringely describes Oracle's Larry Ellison. Quote: "This level of honesty doesn’t make Larry what most of us would think of as a nice person. I once heard him refer to having “nailed” a dinner companion, if you know what I mean and I think you do. But with Larry at least you know where you stand, with most of us standing, frankly, nowhere."
  • A Simple Example That's Incredibly Complex
    Great discussion about Domain-Driven Design (DDD), simplicity, patterns, and other coding aspects. Quote: "If you find yourself writing a sentence with the words “banking, simple, not real-world” you might want to rethinking what it is you’re doing." Great, great article.
  • MacOS X is an Unsuitable Platform for Web Development
    Ted Dziuba describes his experience with MacOS.
  • The State of Video on the Web
    The history behind the <video> tag and its support in various browsers.
Podcasts Programming Security Software
  • AVS Cover Editor
    Create covers for various disc types: CD, DVD, Blu-ray discs, VCD, etc.
  • Contact Sync
    Synchronize contacts between Outlook and Gmail.
  • Dolphin Text Editor Menu
    Adds options to standard text editors (Notepad, OpenOffice.org, Visual Studio, etc) allowing you to sort text alphabetically, reverse line order, remove blank or duplicate lines, change text case, align text, remove HTML tags, remove text formatting, count words and characters, and do more.
  • Dropbox Shell Tools
    A Windows Explorer shell extension to speed up copy or move files to your Dropbox folder (must install as Administrator).
  • Eusing Free MP3 Cutter
    Allows you to cut out pieces of an audio file (MP3, WAV, WMA), keeping ID3 tag info.
  • ExamDiff
    Visual file comparison tool.
  • Lifehacker Pack for Windows: Our List of the Best Windows Downloads
    A yearly snapshot of Lifehacker's favorite applications.
  • KastorSoft: Free Multimedia Software
    Several free applications including video converter, audio extractor and more.
  • PixBuilder Studio
    Yet another free image editing software program for digital photo editing, images processing, and resizing.
  • Sweet Home 3D
    A free interior design application that helps you place your furniture on a house 2D plan, with a 3D preview.
  • UltraSearch
    Searches files and folders on local NTFS drives without using file indexing.
Training
  • INETA Live
    Offers a number of videos on programming and non-programming topics presented at different user groups.
Visual Studio Web design
  • A Basic HTML5 Template
    Explains how to build an HTML5-based to build page template from scratch.
  • Responsive Web Design: What It Is and How To Use It
    Responsive Web design is the approach that suggests that design and development should respond to the user’s behavior and environment based on screen size, platform and orientation. The practice consists of a mix of flexible grids and layouts, images and an intelligent use of CSS media queries. As the user switches from their laptop to iPad, the website should automatically switch to accommodate for resolution, image size and scripting abilities. In other words, the website should have the technology to automatically respond to the user’s preferences. This would eliminate the need for a different design and development phase for each new gadget on the market. This article explains the approach to Responsive Web Design.
Webtools

Friday, September 2, 2011

Learning Adobe Photoshop Lightroom

Summary: Resources for Adobe Photoshop Lightroom newbies.
Recently, I spent $120 on a copy of Adobe Photoshop Lightroom 3 hoping that it would help me improve my post-processing workflow. I'm not a professional photographer, I do not carry expensive digital photo equipment, but I hate low quality (under/over-exposed, grainy, etc) photos, so I mostly rely on photo editing, which -- with more photos in my collection -- was becoming more cumbersome. After reading a number of rave reviews and user comments (e.g. PCMag, ARS Technica, Amazon), I decided to give Lightroom a try.


I had some reservations about using Lightroom on my 3+ year-old laptop with 3 GB RAM and a weak integrated graphics card running 64-bit Windows 7, but it installed and launched fine. My first impression was:
How the heck do I use this program?
I'm not a pro in digital graphics, but I can find my way around most photo editors, including Adobe products. However, my initial attempts to do something useful in Lightroom left me in the state of... what do I call it... frustration... bewilderment. I really had no idea where or how to start, so I finally decided to spend a few hours looking for short introductory tutorials. I will include references to the helpful resources below, but here is my second impression (after I grasped the basics and tried Ligthroom on a couple of hundred photos):
I LOVE IT!
I'm still at the very early stage of learning the basics, but the more I learn about Lightroom, the more I like it.

Here are the features I enjoy (or plan to enjoy) most:
  • Non-destructive editing: When you edit an image, Lightroom does not actually modify the original file; it simply saves your editing steps (which can be quite complex) as instructions (metadata) in a catalog (a catalog contains information, or metadata, about a selected collection of your photos, but it does not include the actual image files). The problem here is that if you lose a catalog file, you will lose all edits of the photos referenced in the catalog (this is why it's important to back up your catalog files regularly). The benefit is that you do not need to keep duplicate files. You can also create multiple virtual copies of the files (pointing to the same physical file) and use them to apply different effects, such as black and white or vignette effects. Lightroom will apply your edits to a new physical copy of the file whenever you export or publish them to a hard drive, your web site, or a photo hosting site, such as Flickr of Picasa.
  • Batch processing: Lightroom allows you to apply the same changes to multiple files in a single step. Why is this important? Because many times you will have similar photos shot under similar conditions. Instead of adjusting them one at a time, you can select a group and apply the same changes once.
  • Before/after shots/previews: Lightroom offers several ways to compare the effects of your editing steps to the original photos, so you can go back and make adjustments. Presets allow you to apply multiple adjustments in a single step. When you roll the mouse over a specific preset, you can see a preview of the preset applied to your photo before you actually make a change.
  • Photo editing: After watching a number of tutorials, I was blown away with the types of adjustments Lightroom supports (and if these are still not enough, it lets you edit your photos in an even more advanced editor like Photoshop CS or Photoshop Elements). I'm using the basic tools (auto toning, exposure correction, clarity improvement, noise reduction, sharpening), and even these produce good results. And since all of the edits are non-destructive, you can always go back to the original.
  • File handling: I always had problems keeping track of my photos. I would copy a new set of shots from a flash card to a dedicated folder, make adjustments, and save the modified files to another folder. Then I may crop the modified files (to 4x6) for printing and save the cropped copies in yet another folder. Messy. With Lightroom, you can easily move photos between different directories without leaving the program. It allows you to rename the files and apply adjustments on import and export.
  • Metadata: I'm yet to start using meta tags (such as keywords) on my images, but when I do, I suspect they would make it easier to handle (search, select, group) photos.
Now, here are some links that I found helpful (if you have additional recommendations, please post them in a comment):

Videos
Blogs
Books

I haven't read any of these, but they seem to be getting the most positive reviews:
Cheat sheets
Presets and plugins
How-tos
Tips and tricks
P.S. I must mention that the help file that comes with Ligthroom is quite helpful, so you may want to start there.

See also:

#CreativeFriday – An introduction to #Lightroom

Thursday, May 26, 2011

Build 32- and 64-bit installers using WiX

Summary: Code samples illustrating how to build deployment packages for both x86 and x64 platforms from the same Windows Installer XML (WiX) project.
This is post #6 of the six-part Learning WiX series. For other topics, see:

Table of contents
  1. Background (why WiX?)
  2. Introduction (answers to common questions)
  3. Getting started (references and tutorials)
  4. How-to's and missing links
  5. Slides and demo projects
  6. Improved demo projects (32- and 64-bit installers)
My recent post offers code samples illustrating a life cycle and core features of a WiX deployment project. The samples miss one important aspect: target platforms (i.e. x86 vs x64). While the samples get installed and run fine on both 64-bit and 32-bit platforms, the deployed applications always appear as 32-bit programs, even though they run as 64-bit processes on 64-bit machines (all assemblies are compiled to run on Any CPU).

I tried to add 64-bit support to the existing projects, but ran into several issues (e.g. folder selection wizard always showed the wrong Program Files folder on 64-bit systems). It took me a few weeks to resolve the problems, so here are the updated projects (all projects use Visual Studio 2010):
These samples do everything the original samples do, plus:
  • Solutions include build targets for 64-bit platforms: Debug (x64) and Release (x64).
  • WiX projects can build 64-bit MSI files (via 64-bit build targets).
  • Build process renames MSI files to indicate target platforms and copies them to a separate MSI folder under the WiX project (in a post-build event).
  • Detect and use the original application folder during upgrades.
Here is what you need to do to build installers for 32- and 64-bit platforms:
  • Understand the differences between 32- and 64-bit installers.
    At the very least, you need to understand that a setup package (MSI file) file can be marked as either a 32- or 64-bit installer (64-bit installer cannot run on 32-bit systems, but it can install 32-bit components on 64-bit systems). The following article can give you a basic idea of intricacies related to 64-bit platform deployment: How Windows Installer Processes Packages on 64-bit Windows (see also other relevant articles).

  • Add the x64 build target to your WiX setup project.
    Assuming that all application assemblies (and support files) are not platform-specific, keep their build configuration marked as Any CPU, but add new configurations to the WiX setup project and associate it with the x64 platform; make sure configuration names identify the 64-bit platform, e.g. Debug (x64) and Release (x64).

    Tip: I often run into problems adding a new configuration via Visual Studio IDE. E.g. sometimes, I cannot add the x64 platform to some projects, or I would add it, but when I close the Configuration Manager dialog box, my settings would disappear. If you run into such issues, I suggest making changes directly to the project (.wixproj) and solution (.sln) files (their structure should be obvious). [Notice that you can edit a project file directly in the Visual Studio IDE by unloading and reloading the project.] Then open the Configuration Manager dialog box (via the Build - Configuration Manager menu), and make sure your project build mappings looks right.
    Your configuration settings should look similar to this:

    The idea here is that you always build your platform-independent assemblies for Any CPU and only use x86 and x64 targets for the setup projects (or any platform-specific project).

  • Define and use platform-specific properties in the WiX source (.wxs) file.
    It's a good idea to have platform-specific GUIDs for product ID and upgrade code, as well as product name. You should always use a variable to store platform-specific Win64 flag and folder names (such as Program Files folder). Here is the code that illustrates how to achieve this:
    <?define ProductName = "WiX Demo" ?>
    <?define ProductVersion = "1.0" ?>
    <?define ProductFullVersion = "1.0.0.0" ?>
    <?define ProductAuthor = "Alek Davis" ?>
    <?define ProductAppFolder = "InstallLocation" ?>
    
    <?if $(var.Platform) = x64 ?>
      <?define ProductDisplayName = "$(var.ProductName) 64-bit" ?>
      <?define ProductId = "47861F89-765F-4D6D-BEDE-139F0BCD74ED" ?>
      <?define ProductUpgradeCode = "EE2511C1-75A7-4954-8AB6-0E405C9481B4" ?>
      <?define Win64 = "yes" ?>
      <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
    <?else ?>
      <?define ProductDisplayName = "$(var.ProductName)" ?>
      <?define ProductId = "490CCCF1-54C3-4AC2-8C88-A8903556EEB3" ?>
      <?define ProductUpgradeCode = "E7E6A7CB-1D12-486D-9E53-DBC56B0EDDCB" ?>
      <?define Win64 = "no" ?>
      <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
    <?endif ?>
    The code above tells the WiX compiler to check the value of the build platform property $(var.Platform). If the compiler detects the x64 build platform target, it will set platform-dependent variables to 64-bit specific values; otherwise, it'll use 32-bit values.

  • Use platform-specific properties to set element attributes.
    Now you can use platform-specific properties set by WiX compiler instead of hard-coded values:
    <Product 
      Id="$(var.ProductId)" 
      Name="$(var.ProductDisplayName) (v$(var.ProductVersion))" 
      Language="1033" 
      Version="$(var.ProductFullVersion)" 
      Manufacturer="$(var.ProductAuthor)"
      UpgradeCode="$(var.ProductUpgradeCode)">
    
      <Package 
        InstallerVersion="300" 
        Compressed="yes" 
        InstallScope="perMachine" 
        Manufacturer="$(var.ProductAuthor)" 
        Platform="$(var.Platform)" />
      ...
      <Directory Id="TARGETDIR" Name="SourceDir">
        <Directory Id="$(var.PlatformProgramFilesFolder)" >
          <Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)"/>
        </Directory>
      ...
      </Directory>
      ...
    </Product>
  • Set platform flag on components.
    Make sure that all of your product components are marked with the appropriate Win64 flag. Use a variable (like $(var.Win64) defined in the code sample above) to change the value dynamically based on the build platform, such as:
    <Component
      ... 
      Win64="$(var.Win64)">
      ...
    </Component>
    If you have components which must be deployed only on 32- or 64-bit platform, you can hard-code their Win64 attribute values and conditionally include or exclude them based on the build target:
    <?if $(var.Platform) = x64 ?>
      <!-- 64-bit components go here -->
    <?else ?>
      <!-- 32-bit components go here -->
    <?endif ?>
  • Rename MSI files to indicate target platform.
    You can define a post-build step to rename your MSI files, so that they reflect the intended platform. Select the Project - Properties menu; in the Build Events tab, set the Post-build Event Command Line to something like this:
    if not exist "$(ProjectDir)msi" mkdir  "$(ProjectDir)msi"
    copy "!(TargetPath)" "$(ProjectDir)msi\$(TargetName)($(PlatformName))$(TargetExt)" /Y /V
    These commands rename and copy the output MSI file to the MSI folder (in the project directory). They will create the folder if it does not exist. The file name will contain the (x86) or (x64) suffix depending in the target platform (e.g. WixDemo1.0(x64).msi).
That's about it. Oh, almost forgot: test, test, test...

See also:
Walking through the creation of a complex installer package by Gabriel Schenker

Monday, May 23, 2011

I don't want to wear the ribbon!

Summary: I'm no Cosmo Kramer, but I also do not like the ribbon.
I keep hearing and reading articles from people announcing how (often after a short struggle) they fell in love with Microsoft's ribbon interface, but after using it for quite a while (mostly in the Microsoft Office products), I still feel more like Kramer:


Is there something wrong with me?

Wednesday, March 2, 2011

Beginner’s guide to Windows Installer XML (WiX) 3.5

Summary: Slides and demo projects.
This is post #5 of the six-part Learning WiX series. For other topics, see:

Table of contents
  1. Background (why WiX?)
  2. Introduction (answers to common questions)
  3. Getting started (references and tutorials)
  4. How-to's and missing links
  5. Slides and demo projects
  6. Improved demo projects (32- and 64-bit installers)
I just updated and uploaded the slides from the presentation I gave to my work group yesterday (you can download the PowerPoint [PPTX] presentation from the SlideShare site):

You can also download the demo projects.

There are three demo solutions, each containing four projects, all implemented in Visual Studio 2010 (you also need WiX 3.5 Toolset/Votive):
  • A client (Windows Forms) application.
  • A server (Windows service) application.
  • A class library (DLL used by both client and server).
  • A WiX 3.5 setup project handling deployment of the client, server, and class library.
The demo apps (client, server, library) don't do much (they are there only for the WiX project). The WiX setup project illustrates how to accomplish the following:
  • Install and configure a Windows service.
  • Install a client application.
  • Install a class library in Global Assembly Cache (GAC).
  • Install a text (readme.txt) file.
  • Create shortcuts under the Start menu for the client app and text file.
  • Create a shortcut under the Start menu for the application uninstaller.
  • Display the advanced setup dialog sequence (wizards) allowing the user to select the installation scope (per user or per machine), specify product destination folder, chose which features to install (client, server, or both).
  • Perform major upgrades (upgrades will retain Windows service definition for the already installed server component).
  • Include application files in the setup package via project references (instead of hard coding the file names).
The three solutions illustrate a typical life cycle of a project. First, you build and deploy version 1.0. Then you can build and deploy version 1.5. The version 1.5 installer will upgrade the already installed version (you don't need to uninstall version 1.0 before installing version 1.5). Then you can do the same for version 2.0. Notice that installers preserve Windows service configuration during upgrades (if you redefine the service to run as a specific user instead of a local system account, the service configuration will remain intact).

UPDATE: See new and improved samples, which explain how to implement installers for 32- and 64-bit targets and do other things.
I'd also like to recommend two books which I found extremely helpful: WiX: A Developer's Guide to Windows Installer XML by Nick Ramirez covers WiX; The Definitive Guide to Windows Installer (Expert's Voice in Net) by Phil Wilson - general Windows Installer (MSI) concept:


And one more thank you to Jeffrey Sharp for his excellent WiX presentation (you can get the slides and watch Jeff's talk online).

See also:
My three-part introduction to WiX series