Wednesday, September 26, 2012

Technobrief #14

Summary: Recent findings of software, articles, and more.
Books CSS Data Design Graphics HTML5
  • Back to basics: how to code an HTML5 template
    Dain Miller covers the basic constructs of HTML5.
  • HTML5Boilerplate
    A template that helps you build fast, robust, and adaptable web apps or sites.
  • HTML5 Form Validation
    Stephen Walther describes how you can take advantage of HTML5 client-side validation regardless of the type of application that you are building.
  • Paul Irish on HTML5 Boilerplate
    In this video, Paul Irish, the man behind the project will show you how you can use HTML5 Boilerplate to get your projects up and running quickly while keeping best practices covered.
JavaScript/jQuery
  • An introduction to Modernizr for designers
    Louis Lazaris introduces a way to ensure that you’re designs are able to take advantage of new HTML and CSS features using the Modernizr JavaScript library and API.
  • Create a Windows-like Interface with jQuery UI
    A walkthrough by Nikko Bautista.
  • Differences Between jQuery .bind() vs .live() vs .delegate() vs .on()
    Elijah Manor attempts to shed some light on confusion about the real differences between these methods and when they should be used.
  • Essential jQuery Plugin Patterns
    Addy Osmani takes the official jQuery plugin authoring guide a bit further
  • Forsight.js
    Gives webpages the ability to tell if the user's device is capable of viewing high-resolution images (such as the 3rd generation iPad) before the image is requested from the server. Additionally, it judges if the user's device currently has a fast enough network connection for high-resolution images. Depending on device display and network connectivity, foresight.js will request the appropriate image for the webpage.
  • Introduction to the ASP.NET Web API
    Stephen Walther offers a brief overview of the features of the new ASP.NET Web API. You learn how to use the ASP.NET Web API to retrieve, insert, update, and delete database records with jQuery. We also discuss how you can perform form validation when using the Web API and use OData when using the Web API.
Life Mobile Opinions Process Programming Software
  • ActivePresenter
    The free edition allows you to create screencasts in three simple steps. Supported formats: AVI, MP4, WMV, WebM.
  • Adapter
    Free video and image converter for Mac and PC.
  • Kaspersky WindowsUnlocker
    A free program that you can use to remove the effects of malware that is blocking you from accessing parts or all of the system.
  • MP4Cam2AVI
    Free MPEG4/MJPEG to AVI converter/joiner for MPEG-4 camcorders and digital photo cameras (also cell phones that shoot video in MP4 format). It repackages video content to AVI container without video recompression, it's fast and loseless.
  • Siren
    Free file renamer.
  • XVI32
    Free hex editor.
Tools
  • Ambient-mixer
    Listen to relaxing music, ambient atmospheres and astonishing sound effects. If you want, you can even create your own atmospheric sound mix, online and for free. Every audio template can be easily edited for your own needs.
  • Compfight
    Image search engine tailored to efficiently locate images for blogs, comps, inspiration, and research. We make good use of the flickr™ API, but aren't affiliated with flickr.
  • jsFiddle
    Offers an easy way to test HTML, CSS, and JavaScript online.
  • Codeacademy
    Great learning resource for teachers and students of computer programming.
  • Marklets
    Bookmarklet search engine.
  • Mockup Builder
    Build mockups of websites and software apps online (free).
  • Invoiceable
    Free online invoicing app (the free version will include a link to the site).
Work UX Windows

Friday, July 13, 2012

How to pass arrays of objects from C# to C++

Summary: Sample illustrating how a C++ code can access a collection of objects returned by a .NET application.
Say you need to make a legacy C++ app call a web service. Since C++ is not very friendly to anything SOAP, the easiest option could be to write a helper .NET app, which would make SOAP calls, and expose it to your C++ app as a COM object via COM interop. This approach is rather straightforward, but it will get more complicated if your .NET COM object needs to pass collections to your C++ app. Here is how you can do it.

Disclaimer: There is no sample project and the code snippets below are incomplete, but they should be sufficient enough for a person familiar with C++ and C# to come up with the final solution. Also, no VB.NET samples, but C# snippets should be easy to follow and convert to VB.NET.
Step 1: Create a C# project for your .NET COM component.
I assume you already know how to do this, but if not, you can start with these articles: Make sure to build a class library (DLL) and check the Register for COM interop option under the project's Build settings. Also, if you do not want to expose every public class, method and property from your assembly (e.g. if it calls a web service, you would not want the Visual Studio-generated proxy classes to be exposed as COM objects), uncheck the Make assembly COM-Visible option in the Assembly Information dialog box (to open this dialog, select the Application tab in the project properties and click the Assembly Information... button).

Step 2: Implement the primary COM component.
Your C++ app will use this COM component's methods to get data. The articles reference in the previous step explain what you need to do, but here are the main points:
  • You need to define an interface and implement a class derived from this interface (component class).
  • Your C++ app will only see the methods declared in the interface.
  • Both the interface and the component must be marked with unique GUIDs.
  • By default, ProgID of a .NET COM component is derived from the namespace and class names; to override it mark your component with an explicit ProgID.
  • I prefer to keep both the interface and component code in the same file, but you can put them in different files.
Here is an abbreviated sample:
using System;
using System.Runtime.InteropServices;

namespace MyCompany
{
    [Guid("11111111-1111-1111-1111-111111111111")]
    [ComVisible(true)]
    public interface IMyComObject
    {
    }
 
    [Guid("22222222-2222-2222-2222-222222222222")]
    [ProgId("MyCompany.MyComObject")]
    [ComVisible(true)]
    public class MyComObject: IMyComObject
    {
    }
}

Notice that we have not defined any methods, yet.

Since our ultimate goal is to pass a collection of objects from C# to C++, I will first implement a method that returns a custom object, and then a collection of these custom objects. Obviously, C++ will not be able to access this object as is, so we need to expose it as another .NET COM object. As with our primary object, the C++ code will access this component via an interface. Keep in mind that C++ does not understand the concept of property, so each property will need a helper getter (and -- if needed -- setter) method.

Step 3. Implement a COM component holding your data.
Let's say your C# app wants to return user info to the C++ app. We'll define a .NET COM object that will hold user properties. In this example, I assume that the C++ code will only need to get object properties, so we will omit COM methods to set them. Notice that we can still set the properties in the C# code:
using System;
using System.Runtime.InteropServices;

namespace MyCompany
{
    [Guid("33333333-3333-3333-3333-333333333333")]
    [ComVisible(true)]
    public interface IMyUser
    {
        [ComVisible(true)]
        int GetIdn();
  
        [ComVisible(true)]
        string GetUsername();
  
        [ComVisible(true)]
        bool GetIsDisabled();
    }
 
    [Guid("44444444-4444-4444-4444-444444444444")]
    [ProgId("MyCompany.MyUser")]
    [ComVisible(true)]
    public class MyUser: IMyUser
    {
        [ComVisible(false)]
        internal int Idn { get; set; }
  
        [ComVisible(false)]
        internal string Username { get; set; }
  
        [ComVisible(false)]
        internal bool IsDisabled { get; set; }
  
        public MyUser()
        {
            Idn        = 0;
            Username   = null;
            IsDisabled = false;
        }
  
        [ComVisible(true)]
        public int GetIdn() { return Idn; }

        [ComVisible(true)]
        public string GetUsername() { return Username; }

        [ComVisible(true)]
        public bool GetIsDisabled() { return IsDisabled; }
    }
}

Now that we have a .NET COM wrapper around our user info structure, let's define methods that return a single user object, as well as a collection of user objects.

Step 4. Implement COM methods in .NET component.
For the purpose of this discussion, implementation of user data retrieval from the back end is not important, so I will abbreviate it. You may get data from a web service, database, Active Directory, or some LDAP store, it does not really matter here. But you will need some way of communicating errors from the C# code to the C++ app. There are multiple ways to do this. I will use an integer return value to communicate error or success code (with zero indicating success) and use a string parameter to pass error information.
using System;
using System.Runtime.InteropServices;

namespace MyCompany
{
    [Guid("11111111-1111-1111-1111-111111111111")]
    [ComVisible(true)]
    public interface IMyComObject
    {
        [ComVisible(true)]
        int GetUser
        (
                int     idn,
            ref IMyUser user,
            ref string  error
        );
  
        [ComVisible(true)]
        int GetUserList
        (
                string    mask,
            ref IMyUser[] user,
            ref string    error
        );  
    }
 
    [Guid("22222222-2222-2222-2222-222222222222")]
    [ProgId("MyCompany.MyComObject")]
    [ComVisible(true)]
    public class MyComObject: IMyComObject
    {
        [ComVisible(true)]
        public int GetUser
        (
                int     idn,
            ref IMyUser user,
            ref string  error
        )
        {
            int result = 0;
      
            try
            {
                // GetUserFromBackend would retrieve data from
                // backend and covert it to a MyUser object.
                // We then need to cast the MyUser object as
                // an IMyUser type.
                user = GetUserFromBackend(idn) as IMyUser;   
            }
            catch(Exception ex)
            {
                // GetErrorMessage would retrieve error
                // messages from immediate and all inner 
                // exceptions.
                error = GetErrorMessage(ex);

                // Set proper return code indicating error.
                result = GetErrorCode(ex);
            }

            return result;
        }

        [ComVisible(true)]
        public int GetUserList
        (
                string    mask,
            ref IMyUser[] users,
            ref string    error
        )
        {
            int result = 0;
      
            try
            {
                // GetUserListFromBackend would retrieve data
                // from backend and covert it to array of 
                // IMyUser objects (each element of the array
                // will actually point to a MyUser object).
                users = GetUserListFromBackend(mask);   
            }
            catch(Exception ex)
            {
                // GetErrorMessage would retrieve error
                // messages from immediate and all inner 
                // exceptions.
                error = GetErrorMessage(ex);

                // Set proper return code indicating error.
                result = GetErrorCode(ex);
            }

            return result;
        }    
    }
}

Now, the fun part: how do we access these methods and handle returned objects from the C++ code?

Step 5: Build type library (TLB) file for .NET COM assembly.
The easiest way to incorporate .NET COM classes in the C++ assembly is by importing the type library. By default, the .NET class library project build does not generate the type library, so add the following to the project's Post-build event command line settings (under the project's Build Events tab):
call "$(DevEnvDir)..\..\VC\vcvarsall.bat" x86 >nul
tlbexp "$(TargetPath)" /out:"$(TargetDir)$(TargetName).tlb" /win32 /silent /nologo

Now, when you build the assembly, Visual Studio will also generate the TLB file along with the DLL. You will need the TLB file only for development.

Step 6: Import type library of the .NET COM assembly into the C++ project.
Add the import statement to the source (or header) file(s) of your C++ project (you can use path relative to the C++ source or header file containing the import statement):
// Set path to .NET COM project relative to the current file
#import "<path_to_project>\bin\Release\<assembly_name>.tlb" no_namespace named_guids raw_interfaces_only

When this line gets compiled, Visual Studio will generate a TLH file with proxy classes for you .NET COM components. The file is located in your user account's AppData\Local\Temp folder. You do not need to explicitly reference this file in the source code or project; Visual Studio will do this for you implicitly. Something to keep in mind: if you make a change to the .NET COM object's public members, you need to recompile the C++ source (or header) file containing the import statement; otherwise, Visual Studio will not see your changes.

Step 7: Create .NET COM object in C++ code.
You can create a COM object in a number of ways: via CoCreateInstance, using smart pointers, etc. Here is how you do it via plain old CoCreateInstance:
HRESULT       hr            = S_OK;
IMyComObject* pIMyComObject = NULL;  // data type matches name of .NET interface

hr = CoCreateInstance
(
    CLSID_MyComObject,       // CLSID_ + name of .NET COM class
    NULL,
    CLSCTX_ALL,
    IID_IMyComObject,        // IID_ + name of .NET COM interface
    (LPVOID *)&pIMyComObject
);

Notice how names of the generated proxy classes, GUIDs, etc match the names you use in the .NET code. If you are not sure what the names are, find the TLH file and see the definitions there. Again, keep in mind that you will make all calls via the interface.

Once you verify that you C++ program successfully crated the .NET COM object, you can invoke the methods.

Step 8: Call .NET COM methods from C++ code.
If you are not sure what the data types of the COM method parameters should be, check their definitions in the TLH file. Normally, Visual Studio does translation correctly between basic data types, i.e. .NET string values will correspond to BSTR strings, .NET DateTime will map to DATE, bool values will turn into VARIANT_BOOL, etc. In this example, I'm interested in the more complex data types used to pass user data collection.

Let's start with the call to get a single user object:
// Error message returned by .NET COM method
BSTR bstrErrMsg = 0;

// Error code returned by .NET COM method
long nErrCode = 0;

// .NET COM object (interface) holding user data
IMyUser* pIUser = NULL;

// hr (HRESULT type) will indicate whether COM call
// was successful
hr = pIMyComObject->GetUser
(
    nIdn,        // numeric identity (key) of the user (int type)
    &pIUser,     // returned .NET COM object
    &bstrErrMsg, // passed by .NET COM method
    &nErrCode    // passed by .NET COM method
);

The code here distinguishes between the (unexpected) errors encountered when making COM calls and (expected) application errors encountered within the called .NET methods. The (unexpected) COM errors may occur due to problems in the environment. For example, if the .NET COM assembly is not properly registered or missing a dependency, the GetUser call will fail. COM errors are detected by checking the HRESULT return code (in this example, the hr value).

But the code in the .NET COM method can encounter an error, too. It can be an unexpected (read, system) or expected (read, application) error. To handle all errors gracefully, I recommend trapping all logic in .NET COM methods within exception handlers. If a method encounters an expected error condition (e.g. the specified user is not found), it should return an appropriate error code (there must be a agreement between the C++ and C# code on the meaning of the error codes) and an optional error message. In case of unexpected exception (e.g. network error when the .NET code calls the backend), the .NET code should set an appropriate error message and return a generic error code (for different ideas on error handling and communication, read my Dissecting Error Messages article in the Dr. Dobb's magazine). Here is an abbreviated example of error handling:
if (FAILED(hr))
{
    // We could not even call the GetUser method.
    // Prosess HRESULT value and get COM error info.
    // Log retrieved error message.
    // Handle error condition.
}
else
{
    // At the very least, we know that we successfully
    // called the GetUser method. But the method itself
    // may have returned an error code and/or message.
    if (nErrCode == 0)
    {
        // Assuming that 0 code means success,
        // we can now acess properties of the pIUser
        // object via the IMyUser interface methods.
        // Do not forget to release this interface
        // when it is no longer needed.
    }
    else
    {
        // At this point we know that we were able to
        // call the GetUser method, but there was some
        // error or problem within this method.
        switch (nErrCode)
        {
            // Handle various error codes.
        }
  
        if (::SysStringLen(bstrErrMsg) > 0)
        {
            // Process error message (log it, or whatever)
            // Remeber to free it when it's no longer needed.
            ::SysFreeString(bstrErrMsg);
        }
    }  
}

I will not show how to use the pIUser object, because it is no different from any other COM object implemented in C++ or any other language.

The most confusing part involves accessing a collection (in our example, this collection is returned from a successful GetUserList call). To make it work, you need to do the following:
  • Define a variable of the type SAFEARRAY* (pointer to SAFEARRAY). You will pass the address of this variable (pointer to a pointer) to the method returning your collection.
  • Upon successful .NET COM call, check the data type of the SAFEARRAY members. If the method returns an array of objects (exposed as .NET COM interfaces), it's most likely that each member of the SAFEARRAY variable will hold an element of the type of VARIANT, IDispatch, or IUnknown interface. Once you detect the correct data type, convert it to a proper interface (in our case, IMyUser interface).
Here is an abbreviated example:
// Array of objects returned from .NET COM method
SAFEARRAY* psaUserList = NULL; 

hr = pIUser->GetUserList
(
    bstrMask,
    &psaUserList,
    &bstrErrMsg,
    &nError
);

// Process errors.
...
// The following assumes that the GetUserList call was successful.

// Data type of the elements of SAFEARRAY elements.
VARTYPE vt;

// Get data type of elements.
if (FAILED(hr = SafeArrayGetVartype(psaUserList, &vt)))
{
    // Cannot detect data type of SAFEARRAY elements.
    // Handle error and clean up.
}

// Check data type of SAFEARRAY elements.
if (vt == VT_VARIANT)
{
    // Each element of SAFEARRAY is a VARIANT.
    // Use a CComSafeArray wrapper class to process
    // elements of the SAFEARRAY.
    // Notice that it holds the VARIANT objects.
    CComSafeArray<VARIANT> saUserList;
 
    // Convert our SAFEARRAY into CComSafeArray.
    if (FAILED(hr = saUserList.Attach(psaUserList)))
    {
        // Conversion attempt failed.
        // Handle error and clean up.
    }

    // Iterate through all items in our collection.
    for (int i=0; (ULONG)i<saUserList.GetCount(); i++) 
    {
        // Use smart pointer to access user objects.
        // Notice that we retrieve the original object
        // from the appropriate member of the VARIANT
        // data structure.
        CComQIPtr<IMyUser> iUser = saUserList[i].punkVal;
  
        // We ccan now acess properties of the iUser
        // object via the IMyUser interface methods.
    }
}
else if ((vt == VT_UNKNOWN) || (vt == VT_DISPATCH))
{
    // All elements of SAFEARRAY are COM interfaces.
    // Use a CComSafeArray wrapper class to process
    // elements of the SAFEARRAY.
    // Notice that it holds IUnknown pointers.
    CComSafeArray<IUnknown*> saUserList;

    // Convert our SAFEARRAY into CComSafeArray.
    if (FAILED(hr = saUserList.Attach(psaUserList)))
    {
        // Conversion attempt failed.
        // Handle error and clean up.
    }

    // Iterate through all items in our collection.
    for (int i=0; (ULONG)i<saUserList.GetCount(); i++) 
    {
        // Use smart pointer to access user objects.
        // Notice that we retrieve the original object
        // directly from the array element.
        CComQIPtr<IMyUser> iUser = saUserList[i];

        // We ccan now acess properties of the iUser
        // object via the IMyUser interface methods.
   }
}
else
{
    // For other data types, add your own handling.
}

Now, was this fun or not?

See also:
Simplifying SAFEARRAY programming with CComSafeArray
SafeArray/idispatch issue
C# ATLCOM Interop code snipperts - Part 1
VARIANTS inside a SAFEARRAY

Thursday, April 19, 2012

Nokia Lumia 900 phone camera problems

Summary: Various issues people reported in reviews of Nokia Lumia 900 Windows Phone camera.
A huge spoiler in my excitement about the new Nokia Lumia 900 came from the phone's camera. When ordering the phone, I assumed that Nokia would build it on the Lumia 800 improvements and offer a camera on par with iPhone 4s. I thought it would be difficult to screw up the excellent Carl Zeiss optics given such great specs. Well, was I up for a surprise.

After shooting a few pictures, I could not believe how bad they looked: lots of noise, bad focus, heavy pixelation, white balance way off (especially in low light), the now infamous pink spot, you name it.

After some digging, I found a Nokia community forum thread where people complained about the same issues. I posted a couple of remarks describing my problems and then I did a bit more research. As reviews of the Lumia 900 kept rolling, I kept discovering more and more complains about the Lumia 900's camera performance.

Some reviewers suspect that the issue is not necessarily cause by bad camera, but most likely is due to software (e.g. overly aggressive JPEG compression). I posted several quotes with references to the sources, but the forum moderator removed them because "[they do] not contain any support question, remark or suggestion relevant to the thread". Fine, I'll include them here. Hopefully, they will help potential users to make a more informed decision. As I find more info, I'll keep updating this post for those who struggle with the camera performance. So, here we go.

From Review of the AT&T Nokia Lumia 900 (WPCentral):
"Where the Lumia 900 camera disappoints is under low-light conditions. Here the front-illuminated sensor shows its weakness versus the preferred back-illuminated sensor (BSI) found on the Apple iPhone 4/4s and HTC Titan, HTC Radar and HTC Titan II. When not using a flash (and even when doing so to lesser extent), image noise is greatly increased resulting in very grainy photos with loss of color and detail. To avoid high noise, you can manually set the Lumia 900’s ISO to 100 or 200 but in doing so, you’ll slow down the exposure speed resulting in a higher chance of image blur from movement of the camera, your subject or both. Because of this, we’re slightly disappointed that Nokia still won’t embrace back-illuminated sensor (BSI) technology. Those sensors can result in more light (photons) hitting the sensor, increasing it from 60% to 90% of the scene. As a result, images in low-light conditions are brighter and have less noise."
From The most disappointing feature of the Lumia 900 is the camera (PhoneDog):
"The camera, which was one of the most glorified features at Nokia's CES press event, is a hot mess. [...] if you aren't outside, the chances of snapping a photo worth keeping are slim. Images begin to suffer from discoloration. If you point the camera at a mostly white surface, you will notice pink spots. In my case, the pink spot is one large, pink splotch that takes up the majority of the viewfinder. That said, once you snap the shot, sometimes the pink spot turns to a dark green tint. Other times, it just disappears completely."
From So my Nokia Lumia 900 is here. I'm not happy (WPCentral):
"Looking at some sample pictures I've seen, it seems like the default camera settings are poorly set. Being a borderline professional photographer and a bit of a camera nerd I would say: For certain it has nothing to do with the lens. Unlikely it has anything to do with the sensor. Problems are with the default camera settings and post processing settings.I would be suprised if Nokia did not improve the settings with a simple patch."
From Nokia Lumia 900 review: Camera (TechRadar):
"It all sounds amazing, but unfortunately the end results don't quite live up to the specs. Contrast and color saturation are above average in most cases, and the Lumia 900 is capable of quite decent outdoor images when the sun is cooperating. We were less enthusiastic about our results shooting indoors, and the dual LED flash didn't seem to do us any favors, either, producing garish results and a lot of red eyes."
From The Great, Bright-Blue Hope (Wired Magazine):
"The 8-megapixel camera (topped by a Carl Zeiss lens) on the rear of the device takes pretty great shots in bright light, on par with similarly specced shooters we’ve reviewed. Performance in low light, however, was less admirable. Shooting pictures in mixed light or in twilight without the dual LED flash caused a large amount of striated noise to show up in the pictures."
From Nokia Lumia 900 vs Apple iPhone 4 (Phone Arena):
"On paper, these two are specifically designed to entice photo enthusiasts thanks to the sensors they’re packing along for the ride, but when it comes down to it, we find the iPhone 4S coming out on top. Essentially, we find it more preferable thanks to its sharper details, natural looking color reproduction, and the fact that it handles exposure significantly better. No doubt that the two can capture some pleasant looking scenery and macro shots, but it’s the iPhone 4S that handles low lighting situations better, since the Nokia Lumia 900’s shots are extremely noisy looking."
From Nokia Lumia 900 or HTC Titan II, is one better than the other? (WPCentral):
"The Lumia 900's camera just under achieves. On paper the Carl Zeiss lens and fast aperture should perform better than it does. To give credit where credit is due, the Lumia 900 does preform nicely outdoors when the lighting is just right. Indoors and when the lighting dims outside is where the Lumia 900's camera begins to fade. I also experienced a little inconsistency with regards to saturation, exposure and focus. Hopefully these issues are software related and can be addressed with an update from Nokia. Otherwise, the Lumia 900's camera won't reach its potential."
From Nokia Lumia 900 for AT&T review: Going all in (GSM Arena):
"The amount of resolved fine detail is not among the best we've seen and there's also lots of noise when viewing the photos at 100% zoom."
From How Nokia Lumia 900 Is Better Than iPhone 4s (And How It Isnt) (iPhone 4s Issues):
"If the Nokia Lumia 900 has any downfalls, it would be two things – the camera and lack of apps. Despite boasting an 8-megapixel shooter with Carl Zeiss lens, we often found the camera software had trouble trying to focus and hence take longer to capture shots. The white balance also wasn’t up to our expectations."
From An iPhone 4S User’s Weekend with the Nokia Lumia 900 (MacLife):
"Unfortunately, the camera is a bit of a weak spot for Nokia here, despite the 8MP sensor which on paper would seem to match wits with the iPhone 4S. It’s a bit of a surprise, considering Nokia devices usually feature better cameras, but clearly they had to cut corners somewhere to get this device to $99.99 with two-year agreement."
From Nokia Lumia 900 – Camera Review (WMPowerUser):
"Despite Nokia’s history with great smartphone cameras, the Lumia 900′s camera falls short of its competitors in many ways. If you’re taking an indoor picture at night, the Lumia 900 suffers from a lot of random blue noise. Anything black and dark in pictures ends up covered with this blue noise, which really reduces the quality of the picture. Nokia turns everything blue for no reason. Outdoor shots were slightly better, but the Lumia 900 fell behind the Samsung Focus S in terms of details when shadows were involved. When I took a picture outside my apartments, the room number in the Focus S’s shot is clearly visible while the Lumia 900 is significantly tougher to read and once again full of noise. [...] The Lumia 900 was better at daytime video recording [...], but indoor video recording at night greatly suffered once again due to that odd blue static. Hopefully Nokia can release a software update to fix some of these issues, but we may have to accept the fact that Samsung can make really great cameras."
From Nokia Lumia 900 Review - Windows Phone with LTE (Anandtech):
"Where the Lumia 900 does seem to struggle is white balance, as pretty much all the Lumias have weird color rendering in the lightbox test with lights on, creating a strange washed out cast. I would wager that this is more an outcome of the older ISP onboard MSM8x55/APQ8055 than anything else, and it's entirely possible that things will get better in later updates as Nokia continues to mess around with the sliders on Qualcomm's ISP. In addition, the preview image sometimes contains the colored center dot chromatic aberration we've seen on other phones, though the lens shading ISP does seem to fix it when you look at the actual captured images. As an aside, this is really another area where eventually moving to dual core SoCs will make a difference - the successors to 8x55 have better ISP."
From Lumia 900 review (The Verge):
"Nokia has a long history of packing terrific optics into its devices, so you would expect that the Lumia would excel in this area. I'm sad to report that it does not. On the device I tested, the rear camera was capable of producing fine photos, though generally the 900 shot somewhat grainy and very washed out images. It's not that those images were particularly bad — they just weren't particularly good. Though the company touts Carl Zeiss optics, I didn't see anything in my results that belied fairly standard smartphone picture-taking capabilities. In fact, the camera software seemed to have real trouble in some settings, with white balance and exposure out of whack compared to my expectations. Additionally, the Lumia 900 produces those dreaded, faint pink spots in the center of the display — particularly visible on bright white surfaces — that we've seen on countless phones. It's not the kind of thing you'd notice in most photos, but you can definitely see a discoloration that shouldn't be there. Now keep in mind, my daily driver is a Galaxy Nexus, which has a relatively poor camera — so this is significant. I went into the Lumia 900 expecting an excellent photo experience, but it's really simply mediocre."
From 8 Days Of Nokia Lumia 900, Day 5: Camera Quality (Paul Thurrott's Supersite for Windows):
"It’s no contest. The iPhone 4S photos are superior, across the board. You really have to look at them, and zoom in the same way with each, but in every single scene I shot, an iPhone version of the photo was superior to the Lumia shots, with crisper details, better color, and less pixelation. Not once did the Lumia come out ahead."
From Nokia Lumia 900 (AT&T Wireless) Review (Laptop Magazine):
"Unfortunately, while you can unlock the camera quickly by long pressing the shutter button, we noticed lag when taking photos. A half-second delay prevented us from capturing the exact group shot we wanted of fidgety kids. The Lumia 900 also had trouble with dim lighting compared with the iPhone 4S; images looked considerably darker and grainier with a blue cast. The dual-LED flash helped but often resulted in subjects with red-eye."
From The Nokia Lumia 900 review (ARS Technica):
"The problem with tying the shutter to the tap-to-focus is that it can be hard to time pictures, especially since the lapse varies depending on the lighting situation. Being able to focus with the camera button is nice, but you don't get to choose what the camera is focusing on. While there are only a few situations that will be hampered by this divide, our problem could be solved by including a setting that lets us untie the shutter from tap-to-focus. [...] The camera has a separate macro focus setting, but we had little luck using it to improve our shots. [...] We noticed the flash seems to trigger in inappropriate situations when the phone could take a perfectly fine picture without it. In the photos of the rainbow mannequin, the flash-less photo appears a bit more washed out, but it was serviceable and more color-accurate. Low-light situations are where the difference between the Lumia 900 and the iPhone 4S cameras emerge. The Lumia 900 was desperate to trigger a flash on the tabletop hockey game in a dim bar, but the situation couldn't really benefit. The setting produces noise in both photos at the top right corner, but the Lumia 900's is more exaggerated, with noise throughout and an unrealistic blue cast to the picture. We tried out the macro setting in the Lumia 900 on a few occasions, but it did little to produce a good macro shot. Even at moderately close ranges, the phone couldn't bring the subject into as good a level of focus as the iPhone."
From Nokia Lumia 900 review (BGR):
"Considering Nokia’s pedigree, I was absolutely shocked the first time I used the camera on the Lumia 900. To put it plainly, as good as the hardware design is on the Lumia 900 is as bad as I found the camera to be. [...] I found that the Lumia 900′s camera takes very poor images [...]. Colors were washed out and didn’t pop at all like they do on the iPhone. The edges of objects were extremely blurry rather than sharp and clear like they are on the HTC One S I have been testing. I also had a great deal of trouble focusing on objects at close range. Even when I tapped on an item to focus on it and snap a picture, the phone still focused on something in the background instead. Macro mode did nothing to resolve the issue. I’m hoping some of these issues will be fixed in upcoming software updates. Considering the quality of images taken using other Nokia handsets, I have to imagine these are not problems with the optics or other camera hardware Nokia used in the Lumia 900."
From Nokia Lumia 900 Review (Ubergizmo):
"Unfortunately, the Lumia 900 does not take the best photos and videos. In my tests, I found that the iPhone 4S, or the Samsung Galaxy S2 (or Galaxy Note) can easily beat the Nokia Lumia 900 in this area."
From It’s Big, It’s Blue, It’s Windows, but Can It Beat Rival Phones? (All Things D):
"The camera, despite having the same resolution as the new iPhone, took notably worse pictures of the same scenes in my tests. To my eye, colors were oversaturated, and details were less sharp."
From A clash of the titans this time round as Samsung's Galaxy Note locks horns with the Nokia Lumia 900 (Know Your Mobile):
"We had high hopes for Nokia's Windows Phone cameras considering the company's history of producing some of the best camera phones. However, it has failed to deliver and Samsung has stepped up to the plate with the Galaxy Note's setup."
From HTC beats Nokia in the camera game with the Titan II (review & gallery) (ZDNet):
"I thought the Nokia Lumia 900 with 8 megapixel camera and Carl Zeiss optics would blow away all the other Windows Phone devices, but sadly it just doesn’t impress me that much. I know megapixels really don’t define the camera, but when combined with a decent aperature and optics the HTC Titan II beats the Lumia 900 in the camera game. It is not just the camera on the Titan II that impresses, but the camera software enhancements that HTC provides help push it past the Lumia 900. You will find panorama mode, burst mode, many different scene choices, red eye reduction, image stabilization, and more."
From Review: Nokia Lumia 900 is the Premium Windows Phone We've Been Waiting (Complex):
"Camera focusing issues: Seems like Nokia’s shutterbug suffers from nearsightedness. Up close shots are money, but the auto focus struggles to frame objects when taking distant photos. It’s great having a dedicated button that automatically triggers the focus, but the feature makes it difficult to time snapshots and there’s no way of setting the focus on a particular background figure."
From Nokia Lumia 900 review (Pocket-lint):
"Our test shots were on the whole good, but we still got mixed results. Basically the more light you have the better the shots. Inside and the flash can be overbearing if your subject is too close, and without the flash the pictures can be noisy. Get the right lighting and the results are very good indeed. [...] The Lumia 900 can take great photos, just don't expect to produce perfect results every time."
From HTC One X vs Nokia Lumia 900 head-to-head review (V3.co.uk):
"The Lumia 900 doesn't do the basics nearly as well with the One X, with its photos regularly appearing washed out."
From Nokia Lumia 900 camera shoot-out vs. iPhone 4 (photos) (C|Net):
[Okay, in this shoout-out, Lumia 900 does okay. I'm including this report for comparison. - AD]
From Не как у всех. Обзор смартфона Nokia Lumia 900 (Вести.ru):
Качество снимков весьма неровное — от великолепного в подходящих условиях освещения и при удачном срабатывании фото-автоматики, до весьма посредственного. Четкость при максимальном увеличении нормальная, а вот цвета зачастую весьма далеки от правды. Во флагманских смартфонах других производителей камеры бывают лучше, достаточно упомянуть iPhone 4S, Samsung Galaxy S III или HTC One X. Видео (съемка Full HD не поддерживается, максимум 720p) получается качественное и с большим битрейтом (до 14 Мбит/с), но страдает время от времени от той же недостоверности цветов, а также от мечущегося автофокуса.
Here are some forums discussing the camera (and other) issues with Lumia 900: UPDATES:
If you struggle with Lumia's camera performance, try these recommendations:
Camera Tip: Taking better photos with your new Nokia Lumia 900 Windows Phone
How to take better photos with your Lumia 900 (read comments)

Some users report much better results when using third-party camera apps:
Widespread camera "focus" issues with the Lumia 900

Wednesday, April 18, 2012

Nokia Lumia 900 phone review

Summary: Impressions from the first few days with Nokia Lumia 900 Windows Phone.
I have been using my new Nokia Lumia 900 Windows Phone for a few days, so want to share my first impressions.
First, some background.

Nokia Lumia 900 is my first smart phone. I spent a few weeks (okay, a couple of months) researching phones and this is what I figured out (I hope this info helps those looking for the first smart phone).

iPhone 4s offers the best overall quality (and camera in particular). Unfortunately, it is expensive (even with a 2-year plan): it would cost me close to $500 for two phones (for me and my wife, including the CA tax on the full price), and I'm not the kind of guy, who shells $500 on cell phones.

Samsung Galaxy S II seems to be the best overall Android phone (again, among other things, I am particularly interested in camera performance), but it also is not cheap. Besides, among the three platforms (iOS, Android and Windows Phone), I like Android the least.

I like the Windows Phone Metro interface, so when Amazon Wireless offered a $19.99 promotion on Nokia Lumia 900 (for AT&T plan upgrades), I took it. With Nokia's $100 credit, I will make about $160 on two phones, so for a cheapskate like me, this was a no-brainer.

My phone came with a free Micro USB Universal Car Charger (thanks, Amazon):
I also ordered Skinomi TechSkin Screen Protector:
The screen protector was easy to install. I noticed several small bubbles, but they disappeared after a couple of days. In addition to protecting the screen, the screen protector makes fingerprints less visible.

And I got a skin case (available in multiple colors):
The case is easy to put on and take off. It makes the phone slightly bulkier, but I hope it will be useful if the phone gets dropped.

Now, back to the phone. Here are my first impressions (before I forget).

Nokia Lumia 900 is a bit bigger than I expected (size-wise, my ideal phone is Samsung Focus Flash 4G), but the size is not overwhelming. It's a pleasure to hold the device in the hand.

I was not sure which phone color to pick. Read a few praises for the cyan version, but decided to stay with the classic black. I'm glad I did. When I saw the cyan at an AT&T store, I was not impressed (it's not bad, mind you, but not my kind of cyan). Now, if a white version were available (it is not, yet, but should be soon), I would get one of the two phones in white.

Despite lower screen resolution (800x480 vs. 960x640 in iPhone 4s), the display looks awesome, even when zoomed. Excellent colors.

The device is quite fast. I have read a few posts lamenting lack of support for dual-core processors in Windows Phone-based devices, but so far, single-core processor performance have been just fine. In fact, it seems to me that Lumia 900 is more responsive (when loading Web pages and using apps) than my top-of-the-line desktop. I'm using the phone over 4G and WiFi (there is no LTE coverage in my area, yet).

I LOVE WINDOWS PHONE OS!!! It took me a couple of hours and a few introductory articles to figure out the basics, but since I have no experience with smart phones, I suspect it would take a similar effort to switch to iOS or Android. The Metro interface looks clean and modern. Very user friendly. I'm not convinced that tiles are appropriate for a desktop OS, but they work great on a mobile phone. Home screen with customized tiles looks very pleasing.

I linked the phone with my Google and Microsoft Live accounts. There was a glitch with Google, so I had to use a workaround to get to my Gmail inbox (the problem went away in a day or so). Email and calendar interfaces are very nice. I use Google Calendar Sync to push updates between my Google account and Outlook, and it works very well.

I took the phone to the AT&T store to complete activation and got the contacts moved from my old phone. Lumia merged my contacts with contacts at Google and Facebook. Finding contacts now is very easy. I really like the feature that allows me to link multiple contacts.

Support for international keyboards came as a pleasant surprise. It took a couple of clicks taps to enable the Russian keyboard. Much easier than I had expected. Great job, Microsoft!

The free Nokia Drive (GPS navigation) app that comes with the phone is quite good, as long as you do not need lane assistance or text to speech. It does not spell out names of the streets, but it gives accurate and timely instructions. I did not have any problem using turn by turn navigation.


I had a bit of a trouble trying to find some apps when using Marketplace on the phone. In particular, I could not find Skype via the phone, even though it was available on the Marketplace website (could be because it was still in beta). Being able to push and install apps from Marketplace on the web (as opposed to the phone) helped.

I have been looking for the left and right arrow keys on the keyboard (so that I could move the cursor in the text fields), until I found an article explaining how to move the cursor. If you need to know: tap on the text field approximately where you want the cursor to move, and if you miss the exact spot (since fingers on small screens are not precise), hold it until you see a cursor marker above the finger, then just drag it to the left or right until it moves to the desired position. Smart, but not very intuitive.

I wish that the Back, Home, and Search buttons were illuminated in sync with the screen. Now, if you use the phone in the dark, you do not see them unless you press one of the buttons. They are not that difficult to find, but it may take a few attempts in the dark, so making the buttons visible would be helpful.

I installed a few apps (Facebook, Twitter, Evernote, Flickr, and more), but several apps were not available for Windows Phone. In particular, my credit union only offers apps for iPhone and Android. So does Mint. I will cover apps and shortcuts in a separate post.

As a long-term Zune user (and heavy podcast listener), I wondered how Windows Phone would deal with my existing music collection and podcast subscriptions. After initial hiccup, it seemed to have worked fine. Nokia synced my collections and subscriptions with my computer. I have not tried to use it concurrently with my classic Zune, yet.

I wish there were an option to select all email messages in the inbox via one tap (instead of manually checking each group).

I also wish I did not have to go through the hoops to view battery indicator.

Call quality is decent. Signal strength is similar to what I had before Lumia. Battery life is as expected. Upon initial and (probably incomplete) charge, it lasted all day of heavy use (I was configuring the phone, installing apps, learning features, etc).

Now, the bad part: despite very promising specs, Lumia 900's camera is disappointing (to put it nicely): lots of noise, bad colors in low light, white balance is off, etc. It may be a software issue, but since Nokia keeps quiet on this topic, it's not clear whether this problem is acknowledged and/or will be addressed. This issue deserves special attention -- at least for those of us who care about camera performance -- so I'll cover it in a separate post.

See also:
Windows Phone site at Microsoft
Get Started with Windows Phone
Microsoft Answers for Windows Phone
Windows Phone: Feature Suggestions
Nokia Lumia 900: your questions answered
Nokia Lumia 900 Facebook page
"I can't even think about switching phones without these apps." Windows Phone 7, a Nokia Lumia 800 and the Essential Apps
72 Windows Phone 7 tips and tricks

UPDATE (Apr 22, 2012): I was using Nokia Drive as a primary GPS navigation system on a recent trip to Bay Area, and about a mile before I needed to make a turn on a freeway, my phone froze: the GPS screen got locked in the same position, the phone did not respond to any actions, I had to press and hold the power button for about 10 seconds to perform hard reboot. Once I realized that my position on the GPS display was not moving, I have passed my exit, and it took me about 20 minutes to return to the route. Can't say if this was an issue with the phone (hardware), Nokia Drive app, Windows Phone OS, or any combination of the three.

A few other observations. Accidentally dropped the phone from about 3 feet on a concrete floor; it's good that it was in a case; no damage detected. It's easy to accidentally tap the Search button when using the camera. Calendar reminder does not have an option to snooze to be reminded again in whatever minutes before start (as Outlook does). Still no option to change the playback speed when listening to podcasts. Answering a call is a two-step process; I wish it required one tap. I had to look for and watch a short video to learn that to close an app, you need to tap the Back key. There is no increasing ring volume option (i.e. when the ringer starts at low volume and gradually increases to the specified level); seriously? I have been using this feature in every cheap non-smart phone I ever had; come on Nokia/Microsoft. I cannot find a way to switch into a meeting mode (i.e. change ring to vibration) in a single step (a shortcut would be nice).

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