I've only found one way to lock down code very well. Almost every form of serial validation can be cracked easily by your average second-year programmer.
The way I've done it is to use a License object in .NET. Within my home-grown license object, it reads a "license" file to find out where "home" is. That license is an encrypted string. The private key to the string is in the License object.
The License object then calls home with a secret password, also encrypted. The server decrypts the password and validates it... also logging the IP and user name in case of fraud investigation. If the server can validate the password, it responds with a secret response, encrypted again so that it cannot be spoofed. If it cannot be validated, the connection is dropped. No response is sent, thus the License object at the other end fails.
When the integrated License object fails, it will automatically throw an exception, forcing the application to fail and exit at the point that the license is called.
It took me about two work-days to write the server and the License object, so it's a bit of a workout, but not rocket science.
If you want some sample source, or more info, let me know. I'll be glad to get you what I can.