9/29/2007

Bill Of Rights for Internet Users?

I just have to laugh at this one. OpenSocialWeb.org has a "Bill of Rights for Users of the Social Web". Wait a minute, guys -- didn't we skip over something here?

Forget about the "Social Web" Horse Manure! I got more important stuff to worry about -- How about an INTERNET USER'S BILL OF RIGHTS? I guess my needs are just a lot less "Social" than yours, pals.

Now this is a step in the correct direction, IMHO. But there is more, much more:

  • How about including rights to control your own computer?
  • Shouldn't the practices of installing spyware and other software that cannot be controlled by the owner/user be legally considered trespass or assault of some kind?
  • What gives anyone the right to take control of my computer without my consent?
  • How about offensive advertising on web sites that I cannot 'opt out" of?
  • What about SPAM emails that I get?
  • What about Cellular providers that censor text messaging?
  • How about people who get shut off by their ISP with no ability to have any kind of arbitration procedure if they've been falsely accused?
  • How about web site owners that get their AdSense accounts shut off by Google with little to no recourse because of "Big Brother" Gestapo tactics where they have no due process?

You get my drift? It's a jungle out there and the list could be much bigger; I'm just trying to seed the random Rant Generator. I hope it works...

Have fun.

9/27/2007

Convert a Visual Studio 2003 Class Library Project to VS 2005 Web Application Project

This one came about because an asp.net forums user was having difficulty understanding how the Web Site project model works vis-a-vis the Web Application Project model works. Under almost all scenarios, I recommend the Web Application Project model - especially if you intend to deploy the resultant build into production and it is not just a "demo".

The OP wanted to have a single assembly - something your Web Site Project simply is not geared to do. I explained that you can put all your class library and control *cs or *vb files into a WAP, and they all get compiled into a single dll.

After the OP got my explanatory answer post, another user posted an elegant set of instructions as well, which I reproduce here:

How to convert VS2003 Class Library project to VS2005 Web Application project

1) Open 2003 Solution in VS2005
2) Run Conversion Wizard
3) Close VS2005
4) Open Class Library project in Notepad
5) Replace line:
<ProjectType>Local</ProjectType>
with
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
6) Save project file
7) Re-open Solution in VS2005
8) You'll notice that the project is now a Web Application project
9) However, you need to convert all the WebForms and UserControls to the new code-behind model
10) In Solution Explorer, hilight the .aspx and .ascx files (you can select multiple)
11) Right click and select "Convert to Web Application"
12) You should now see that your code behind files have been converted to partial classes and all the control references have been moved to a .designer.cs file.
13) Congratulations, your project has now been converted.

9/24/2007

Story of a Successful Web Site

It was about June of 2000. Robbe Morris and I were working together at an outfit in Maitland, FL called Sprint TeleCenters. We did most of our programming in VB 6.0, or VBscript. (Uggh!)

I distinctly remember that I was probably a bit more into Web programming at the time than Robbe was -- I was on the SOAP distribution list, avidly reading all the rants of the likes of Dave Winer and Don Box. They were excited about what they were doing with SOAP, and I was excited. When I'd talk to other people about XML, often I'd get comments like "Why on earth would people want to mess with all that glop!" and so on.

One day, I said to Robbe, "Let's start a developer website with articles". Now this is a guy that, at the time, I don't think had written a line of HTML in his life. But, he liked the idea. His Significant Other came up with a crazy name: "eggheadcafe". I thought it was great (and of course, so did he) so we decided to run with it.

Within 2 weeks, Robbe was coding more advanced classic ASP with VBScript than I ever had done. He had grand plans - he got a server and set up colo hosting, we got the domain, and eggheadcafe.com was born. We started out with a very simple UI (it seems quite garish in retrospect, but what did we know?) and we started publishing some articles. The basic theme was "HOW TO" - with code samples. We knew nothing about standards, nothing about SEO (Search Engine Optimization). All we knew is that we thought we had a good idea, and we were going to run with it.

Here is a link to the Wayback Machine's copy of our site in Dec, 2000!

By the end of the year 2000, we had already done plenty of experimentation. Later we started putting in advertising. We never made any real money, but we kept revising the formula and experimenting and getting metrics with Webtrends or whatever other scripts were available. So, it was really all a big experiment at the time.

Then about 2004 we added Google Adsense, and saw some immediate results. For the first time, we were getting contextual advertising that made sense, and users were clicking on the ads. Checks started coming in each month.

The site has gone through at least 4 major iterations that I can think of, the last one being a major conversion to ASP.NET 2.0 that Robbe engineered, and we've spent a lot of time monkeying with the UI, with advertising, and with how to get good metrics.

But the overriding premise -- which has not changed at all since our inception in 2000 -- was to provide GOOD CONTENT. We decided early on that we wanted to be more of a "boutique" site than a "big, everything" site.

By 2005, eggheadcafe.com was bringing in a monthly income equivalent to a full time professional career -- for not one, but two families. It's continued to grow, up to today. We've always avoided what I refer to as "in your face" advertising - you know what I mean -- those interstitials, the popups and popunders, the ads that move across the screen and blot out what you're trying to read that drive you nuts, and the double-underlined contextual ads that pop up some irrelevant keyword-based window that you have to stop and figure out how to get rid of. Yikes! What are people thinking, man?

I really cannot understand how some companies believe this kind of in your face advertising will make them more money. In fact, at one point I was going to compile a list of all the sites I'd recommend "not to visit" because their ads were so offensive. People come to a site because it has content, and they return if the content is good and useful. If you have unobtrusive advertising that blends well with the content, and people are interested, they will click on the ads. You do not need to interfere with their visiting experience at all to have advertising that works. In case you may be interested in what advertisement features users "hate the most", I present a table of the worst offenders from a respected study:


  • Design Element / Users Answering "Very Negatively" or "Negatively"


  • Pops-up in front of your window  95%

  • Loads slowly  94%

  • Tries to trick you into clicking on it  94%

  • Does not have a "Close" button  93%

  • Covers what you are trying to see  93%

  • Doesn't say what it is for  92%

  • Moves content around  92%

  • Occupies most of the page  90%

  • Blinks on and off  87%

  • Floats across the screen  79%

  • Automatically plays sound  79%



And, while we are certainly interested in keyword analysis and SEO, that's never been our primary focus. We want to provide content. That's the right formula!

So, the formula for success is simple:

1) Create good, useful, unique CONTENT. If it is a subject that you yourself are excited about, so much the better.
2) List your site with major search engines and blog directories. Use a sitemap xml file.
3) Put in quality advertising - not "too much advertising" - that doesn't interfere with a quality user experience.
4) Keep good metrics and test, and keep what works.
5) Don't be afraid to experiment.
6) Don't give up -- it can take a long time to develop a successful site.

9/19/2007

Incompatibilities between Framework 2.0 and 3.0 versions on Vista vs XP

The human mind treats a new idea the same way the body treats a strange protein; it rejects it.
- PB Medawar

Hans Passant, an MVP and moderator of one of the MSDN Forums, found some interesting items:

The .NET 3.0 version of the framework as installed on Vista is not the same as the one installed on XP SP2. On Vista, the setup utility overwrites all the V2.0 assemblies and upgrades them from version 2.0.50727.42 to 2.0.50727.312.

There are some differences between the 42 and the 312 revisions of mscorlib.dll and System.dll. There are indeed a few places where 312 explicitly checks for Vista. There are several places where a SecurityPermission attribute is changed from InheritanceDemand to LinkDemand. And what looks like bug fixes in PerformanceCounter, FileWebRequest, RuntimeMethodInfo and UdpClient. Hans says that WaitHandle is the most visible class with new exceptions being thrown.

There's also a potentially breaking change in HttpServerUtility that was discovered by Juan Llibre. It has now acquired a TransferRequest() method that is not present in the 42 revision. If you use this method, your code will not run on XP. The online version of the MSDN library appears to be accurate and up-to-date for the Vista version.

In the V3.5 Beta1 version of the framework, its setup utility silently overwrites the 42 revision with a 1318 revision. The changes in mscorlib and system are far more intense with many bug fixes.

I post this information because recently a user on our eggheadcafe.com forums complained that the Directories.GetFiles() overload that accepts SearchOptions.AllDirectories parameter fails on Windows Vista with "phantom directory" exceptions like:

TYPE: System.UnauthorizedAccessException
MSG: Access to the path 'C:\Users\david\documents\My Videos' is denied.
SOURCE: mscorlib
SITE: WinIOError

Most likely, because the app was built on a Windows XP machine...

Umm, I'm glad we're doing bug fixes -- but when we start breaking cross-OS versioning in DotNetFx, then I start getting a little nervous.

9/17/2007

KB929729 Windows Update Failure - An Easy FIX

OK, it's Microsoft Windows Update "Fun Time" again! KB929729 Security Update for .NET 1.1 shows up in Windows Update and guess what? It never goes away. It's like it's going to be there wanting to get reinstalled like FOREVER.


The 1.1 service pack was an optional update that many users did not install, so the latest security update is doomed to fail. The security update did not search for the right version prior to installation so either you got a installation failure message or it "updated sucessfully" only to reappear as a needed update a few minutes later. If you are unfortunate enough to have .Net Framework version 2 or 3 without updating your service pack for version 1 your headaches just got worse, because the official "FIX" for this involves uninstalling ALL versions of .NET Framework and is quite painful. Fortunately for many the shorter "Fix" I detail here should work. NOTE: This is for Windows VISTA ONLY.

1) Instead of using Windows Update, download the KB929729 package from here:
http://www.microsoft.com/downloads/details.aspx?FamilyId=7EEA368D-7B82-4583-8537-30351718A4E9&displaylang=enhttp://www.microsoft.com/downloads/details.aspx?FamilyId=281FB2CD-C715-4F05-A01F-0455D2D9EBFB&displaylang=en
Do not install yet.

2) Run cleanup tool:
http://astebner.sts.winisp.net/Tools/dotnetfx_cleanup_tool.zip

and select remove (cleanup) .NET Framework 1.1 from the dropdown list.

3) Download the .Net framework 1.1 redistributable package from here
http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=262d25e3-f589-4842-8157-034d1e7cf3a3

and install it.

4) Download the .Net Framework 1.1 Service Pack 1
https://www.microsoft.com/downloads/details.aspx?familyid=A8F5654F-088E-40B2-BBDB-A83353618B38&displaylang=en
and install it.

5) Finally, launch the KB929729 installer and it will work:


9/15/2007

How can I Install Membership, Roles and Profile on my Hosted Site?

This one (or variations of it) has come up frequently in the last week or so on ASP.NET newsgroup and forum posts, so I thought it would be appropriate to take another stab at providing some help.

The first thing we want to do is to enable our database for the providers. There are actually three separate ways you can do this:

1) Run ASPNET_REGSQL and follow the prompts. Of course, this requires that you have command-prompt access to the machine on which your site will be deployed. For hosted solutions this is obviously not available to us. "Where is 'ASPNET_REGSQL.EXE'", you say?

You know the old Chinese proverb about "teach a man to fish?" BTW, when you do find it, you'll of course know that as with most command - line utilities, you can type /? after the name of the executable at a command prompt and see a help listing of commands.

2) Run ASP.NET_REGSQL.EXE on your own sample database locally, and EXPORT all the sql script necessary to create the tables, views and stored procedures on a remote site. Again, this assumes you have the ability to access a remote hosted site's SQL Server online and execute a rather large SQL Script on it. If you take a look at the command line options for this, it has an export to file option.

3) Do it programmatically! Make a "Setup.aspx" page that uses the System.Web.Management utility method:

Management.SqlServices.Install("server", "USERNAME", "PASSWORD", "databasename", SqlFeatures.All)

System.Web.Management -- SqlFeatures.Install Method

Here is the signature:

public static void Install (
string server,
string user,
string password,
string database,
SqlFeatures features
)

Members:
Member name Description
All All features.
Membership The membership feature.
None No features.
Personalization The personalization feature.
Profile The profile feature.
RoleManager The role manager feature.
SqlWebEventProvider The Web event provider feature

This enumeration has a FlagsAttribute, which means you can select two or more features by combining them with the & (and) operator (the And operator in Visual Basic).

This does everything that ASPNET_REGSQL does. You'd think they would make it more obvious that you can do this, given the large number of sites that are hosted by commercial shared hosting companies, but no, they decided to push ASPNET_REGSQL as if everybody everywhere automatically has access to it. Go figure. There is a sample page "SetUpASPNetDatabase.aspx" in the sample solution in this article where you can simply fill in the above parameters in a form, and press a button to set up your database.

Have fun! It would be great if the documentation for basic "how to" or the ASP.NET Quickstart info would fill you in on this stuff. They don't.

9/13/2007

We Have Met the Enemy and ...

"Strategy without tactics is the slowest route to victory.
Tactics without strategy is the noise before defeat."
-- Sun Tzu

I have no issue with people who want Microsoft (and others) to make full disclosure about "Stuff" they do (or don't do) with your PC when you subscribe to Windows Update and similar services. That's just good citizenship to me.

But it's possible to go overboard on this vigilante stuff. Here is an example:

Microsoft updates Windows without users' consent

This guy is basically making a big hullaballoo about Microsoft wanting to update Windows Update itself, in order to make it better and fix any bugs, but he's making it sound like it is some sort of "Stealth Install" that violates your privacy rights. Then at the end, he tells you not to roll back any of the updated files. Umm, who's kidding whom, pal?

Oh, and here's another on eWeek from another Linux fanboy who's simply parroting the first guy and then mindlessly embellishing it. Yikes! Wait a minute --look at the file extension on that page! These guys are still running Classic ASP? Jeesh! What about LAMP, already, if we gonna promote Linux Desktop? Oh, the hypocrisy! It's just never-ending. These guys aren't experts --they're advertising sponsor whores! The first guy's junk made me choke on my fyookin' bagel and I almost forgot my mantra, d00d!

The first guy also quotes a conversation with PSS which proves this is completely innocuous:

"7.0.6000.381 is a consumer only release that addresses some specific issues found after .374 was released. It will not be available via WSUS [Windows Server Update Services]. A standalone installer and the redist will be available soon, I will keep an eye on it and notify you when it is available."

What's our problem here, "Windows Secrets" -- don't have anything really relevant to write about, so you have to resort to scaring people with "fake news"? Less is more, d00d. If you can't come up with something intelligent and useful to write, then don't write anything.

Get real. Maybe the New York Times will give me a "family discount" on a full page ad...

9/09/2007

SpamBot Killers on the Rampage

"Am I afraid of high notes? Of course I am afraid! What sane man is not?” -- Pavarotti

If you run a blog or website and you are carefully watching your hits, more often than not you should be able to identify bots attempting to do all kinds of nasty, evil things at your site.

Many of these absolutely will not obey the Robots.txt file (although they may request it just so they can see if there is any material for nastiness there). They perpetrate all kinds of schemes, trying out SQL Injection attacks, attempting to post blog spam and trackbacks, and the like. Some of the stuff they try (and I have real-time database logging, so it's easy to amuse myself) is either hilarious or pitiful - depending on your frame of reference!

Fortunately, it is relatively easy to head these nasties off at the pass -- almost all this junk traffic emanates from IP addresses that don't change. So what I do is I have a BADIP list that's easy for me to edit, and in Global, in Application_PreRequestHandlerExecute, I have an "IsBannedIp" method that does a lookup on my list and grabs the request and kills it before it even gets to any Page handler. Of course if you wanted to get really nasty in return you could initiate the Ping of Death on them or some such foolishness, but I would not recommend it. The best thing to do is head them off at the pass. This won't stop the requests, because they are trolling Weblogs.com and other RPC services for "new stuff" to do their nasties on all the time. But, at least they don't get in.

Here is some sample code that does the trick -- all this is in global.asax:




// global--

public static string[] BannedIPs;
protected void LoadBannedIPs()
{
string strBanned = ConfigurationManager.AppSettings["bannedIPs"];
BannedIPs = strBanned.Split(';');
}


protected void Application_Start(object sender, EventArgs e)
{
LoadBannedIPs();
}


public static bool IsBannedIP( string strIP)
{
bool retval = false;
foreach(string s in BannedIPs )
{
if(strIP==s) retval = true;
}

return retval;
}

protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
if ( IsBannedIP(Request.UserHostAddress))
{
PAB.ExceptionHandler.ExceptionLogger.HandleException(
new Exception("Banned IP:" + Request.UserHostAddress.ToString() + ": " +
Request.RawUrl +":UA=" +Request.UserAgent) );
// this ought to fix them...
HttpContext.Current.Response.Redirect("http://www.yahoo.com");

}
}


The "PAB.ExceptionHandler" code you see there is just my custom logging facility into the database. Here's one of the baddies that's on my banned list: 170.224.8.126. If you do a google search on the IP address you will see it show up in a lot of logs that have identified it as a nastybot. You do want to be careful with stuff like this though. I once put the googlebot into my baddies list by mistake. If you log your hits with user-agent, IP Address, referer and other key information and keep it in a database table that's easy to view with an admin page, that makes things much easier. Especially, you want to be able to look at the entire request URL including querystring, since that's what will really tell you whether it's got good intentions or not.

This is kind of the next worst thing than outright plagiarism of your content. Just last week I emailed somebody who'd copied a blog post of mine and republished it verbatim -- minus any trace of attribution to the source. I told the guy, "look - if you are going to lift other people's content, at least take the time to massage it around and add some value, OK?"

Such is life on the Internet.

9/07/2007

The specified string is not in the form required for a subject

At one of my "Playground" sites, BlogMetaFinder.com, I get an approval email every time somebody submits a blog or website to the directory. ( If you have any kind of  blog or website related to blogging I encourage you to submit it there).  All submissions go into the database, but they are marked as "Active=0" until such time as i review the submission via a link to a special "Admin" page that I get in the email. It only takes me a second to inspect a site that represents a  legitimate submission (e.g., not porn, not drugs, no redirects, no funny business script kiddie bullshit, etc.) and put the form into edit mode, mark the "Active" checkbox, and update the record which makes it activated into the directory immediately.

However, you always get the occasional bot or bullshit artist that wants to try and see if they can pull a fast one, and they submit junk in the hopes that my directory isn't smart enough to stop them. Invariably, the subject line of the email I get, which includes their submitted site title, has B.S. in it. The result may be that the System.Net.Mail namespace throws an exception, and usually the culprit is the subject line as mentioned in the title of this post.

Here is an easy fix:

subject= Regex.Replace(subject, @"[^ -~]", "");

9/06/2007

Session_End, where is my Request?

This is one of the most common misunderstandings about ASP.NET -- so common that I even blundered it myself in a newsgroup post, even though I know better. A typical question might go like this:

"I have code in my session_start to create a temp directory for the session. On session_end it is supposed to delete the directory but doesn't seem to be firing. I've enclosed the code below. Can anybody give me any suggestions as to why Session_End seems to not be being fired?"

<sample code from the OP>

Session_End only fires for InProc Session mode, and it fires on the server. It is completely independent of any "Request" -- in fact that's the reason it fires - because there *IS* no Request and there hasn't been one for some time!

So -- the concept of getting anything out of a Request when Session_End fires is a moot issue.

9/05/2007

Like a little Assembly with your GAC?

Q: Is it posssible to get an assembly out of the GAC to another folder? Let's say you GAC-ed an assembly but you deleted the source by accident?

A: The assembly would be stored in this folder <windowsdir>\assembly\GAC\<Assembly Name>\Version_PublicKeyToken. You can get that using the copy command from the command prompt and using "CD dirname". From Windows Explorer, you cannot do it as Explorer has a custom view of the windows\assembly folder that hides the actual files and subfolders. That's for 1.1 Assemblies. 2.0 Assemblies would be in <windowsdir>\assembly\GAC_MSIL\<Assembly Name>\Version_PublicKeyToken.

Q: GAC_32, GAC_MSIL, GAC_64 - What gives?

Specifically why is there is a GAC_32 and a GAC_MSIL folder? Is it because some assemblies are compiled to x86 machine code (GAC_32) and some assemblies are compiled to MSIL? What dictates where your assembly ends up?

A: The GAC_MSIL cache contains assemblies that can be run in either 32-bit or 64-bit mode, and are JIT compiled to the required word size as needed.

The 32/64 directories contain assemblies that are specific to either 32-bit or 64-bit mode, either because they contain native code or because they make specific assumptions about the word size.

A 32-bit system will only have the GAC_32 directory. A 64-bit system will have both because 32-bit code is supported via emulation (WOW32).

This distinction is new to .NET 2.0, so all the above applies to .NET 2 assemblies. There may be another directory called just "GAC" that contains .NET 1.x assemblies.

Q: What's "GAC"?
A:
We'll let Wikipedia have that one.