Sunday, 16 May 2004

A Blog with Brains? Joel on Software

I infrequently find myself on Joel Spolsky's site as the result of a search for something or other. It's not really a blog but a collection of articles on many different (it/web/developer) topics. The stuff he writes is ALWAYS interesting.

With that in mind, I finally added his Article Archive to the list of sites I visit regularly, just to see what's there.

I don't know how I missed this article previously, given it's relevance to me and my work, but The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) is my 'read of the week'.

Saturday, 15 May 2004

More on... Client-Side Validation in Downlevel Browsers

This article ASP.NET.4GuysFromRolla.com: Client-Side Validation in Downlevel Browsers summarises the issues with validation in 'downlevel' browsers [although calling them 'downlevel' is Microsoft techmarketing at it's best]. Why not call them 'more standards compliant than IE' browsers instead?

More and more clients will be using non-IE browsers (Safari, Firefox, Mozilla) and non-PC-devices in future, so it makes sense to try and provide the same user-experience (ie. faster, client-side validation).

Go to the article to finds links to standards-DOM-based validation controls; or the longhorn beta documentation, which contains simplified versions of the validator controls that ship with the .NET Framework. Unlike the validator controls in the SDK, which work only with Internet Explorer, these controls comply with the World Wide Web Consortium Document Object Model Level 1 specification (W3C DOM Level 1) and support a number of browsers such as Internet Explorer 5, Netscape Navigator 6, and Opera 5.

Thursday, 13 May 2004

Automating Word with C#

The Microsoft Word MVP FAQ is a useful reference, however it's annoying that so much Office Automation code on the web is still in VB/VB.NET. It appears very few people are using C# for automation projects (so far).

Also, there is no more frustrating phrase in programming than finding this: Microsoft has confirmed that this is a problem in the Microsoft products that are listed at the beginning of this article on MSDN in relation to a problem you are having, like automating OrgCharts in Excel. Argh!

Wednesday, 12 May 2004

HttpHandlers and HttpModules

Both these articles are very useful if you want to understand the handler/module concepts, and get some sample code.

You'll want to know about this if you are doing URL-rewriting, sophistication accept-language-detection or other early-in-the-pipeline processing.

Monday, 3 May 2004

Unicode to Html Entity and back again

WorldPay with C#/.NET [2] also discusses encoding "double byte" (unicode, really) values as Html Entities. The 'final' code is
private string HtmlEntityEncode (string unicodeText) {

int unicodeVal;
string encoded="";
foreach (char c in unicodeText) {
unicodeVal = c;
if ((c >= 49) && (c <= 122)) {
// in 'ascii' range x30 to x7a which is 0-9A-Za-z plus some punctuation
encoded += c; // leave as-is
} else { // outside 'ascii' range - encode
encoded += string.Concat("&#",
unicodeVal.ToString(System.Globalization.NumberFormatInfo.InvariantInfo), ";");
}
}
return encoded;
}


But it's also fairly easy to get your 'original' string back... this code can go anywhere
System.Text.RegularExpressions.Regex entityResolver = 

new System.Text.RegularExpressions.Regex (@"([&][#](?'unicode'\d+);)|([&](?'html'\w+);)");
string outputString = entityResolver.Replace(inputString,
new System.Text.RegularExpressions.MatchEvaluator (ResolveEntity) );

as long as this method is available
private string ResolveEntity (System.Text.RegularExpressions.Match matchToProcess) {

string x = "X"; // default 'char placeholder' if cannot be resolved
if (matchToProcess.Groups["unicode"].Success) {
x = Convert.ToChar(Convert.ToInt32(matchToProcess.Groups["unicode"].Value) ).ToString();
} else {
if (matchToProcess.Groups["html"].Success) {
switch (matchToProcess.Groups["html"].Value.ToLower()) {
// this could be expanded to as many as you like, or (maybe)
// System.Web.HttpUtility.HtmlDecode will work on
// the whole 'entity' string... ?
case "nbsp": x = " ";break;
case "copy": x = "(c)";break;
case "lt": x = "<";break;
case "gt":x = ">";break;
case "amp": x = "&";break;
}
}
}
return x;
}


UPDATE: list of HTML 4 entities which could be used to write a robust ResolveEntity() method using the 'pattern' abpve/

'Securing' data on client round-trips

When my website users go 'off-site' I want to set some information that I can check later, with some confidence that they have not altered it.

OK, I can put it in a Session variable, but I don't have Sessions enabled. What I want to do is encrypt a little chunk of data which can be added to the ViewState, hidden FORM field or Cookie (ie. it will be "client/transport implementation independent"), and check it again later on...

I think these two articles: String Encryption With Visual Basic .NET and Building Secure ASP.NET Applications: Authentication, Authorization, and Secure Communication might be what I'm looking for... perhaps using Base64 if there's binary data being generated.

If I get it working I'll post it here (as always)

Tuesday, 27 April 2004

DHTML JavaScript Tooltips

I use ToolTips a lot in my web apps, not just for 'help' information but also hovering over rows in a table to present additional information rather than having super-wide tables/lots of columns.

This DHTML JavaScript for Cross Browser Tooltips is the coolest implementation I've come across...

Thursday, 22 April 2004

ASP.NET Browser Detection - uplevel FireFox?

I'm so impressed with the latest (0.8 last I checked) release of FireFox that I've started using it to test my ASP.NET apps.

Therefore I was pleasantly surprised to find someone(Rob)'s already figured out
Browser Testing and Detection Resources to encourage ASP.NET to play nicely with the latest IE-equivalent browsers...

Thanks to Mitch's blog on Web Controls and Browsercaps for pointing me there.

Wednesday, 14 April 2004

C# and Excel Automation : Google Groups to the rescue

I don't post to newsgroups that often (although I try to contribute as much as I can to asp.net), but whenever I do I'm always amazed (and thankful) at the speed and quality of responses.

Overnight my problem with Excel automation was solved
Google Groups: Looping in Excel XP with C# -- Ranges and SpecialCells - reducing processing time from 90 minutes to 3 minutes!

Here is the 'working' code; loops through the Areas returned by the SpecialCells method so we only process non-empty cells...

Excel.Range range = sheet.UsedRange;

Excel.Range newrange = range.SpecialCells(Microsoft.Office.Interop.Excel.XlCellType.xlCellTypeConstants, (object)3 );
for (int areaid = 1; areaid <= newrange.Areas.Count; areaid++){
Excel.Range arearange = newrange.Areas.get_Item(areaid);
for (int row = 1; row <= arearange.Rows.Count; row++){
for (int col = 1; col <= arearange.Columns.Count; col++){
cell = (Excel.Range)arearange.Cells[row, col];
// do stuff with cell.Value2
}
}
}
FYI, the SpecialCells (object)3 parameter is the sum of relevant 'constants' below:
XlSpecialCellsValue 

xlErrors 16
xlLogical 4
xlNumbers 1
xlTextValues 2

Tuesday, 6 April 2004

Automating Excel with C#

After much hair-pulling because I could not make Excel "work" as it should, I came across this article 328347 - PRB: "Member Not Found" Error Message When You Use a For Each Statement on an Excel Collection with Visual Basic .NET or Visual C# .NET

So that's why I couldn't loop through cells!

This seems to work better:
if (m_Excel.Workbooks.Count >= 1) {

Excel.Sheets sheets = book.Worksheets;
Excel.Range range,cell;
// foreach (Excel.Range cell in range.Cells ) BROKE http://support.microsoft.com/?kbid=328347
foreach (Excel.Worksheet sheet in sheets) {
range = sheet.UsedRange;
for (int row = 1; row <= range.Rows.Count; row++){
for (int col = 1; col <= range.Columns.Count; col++){
cell = (Excel.Range)range.Cells[row, col];
if (null != cell) {
if (null != cell.Value2) {
cellContent = cell.Value2.ToString();
// do something with the text...
}
}
}
}
}
}


Incidentally, I still don't know why ".Value2" ...

Monday, 5 April 2004

File Extension filtering with .NET Regular Expressions

The 3 Leaf: .NET Regular Expression Repository provided a great starting point for writing a file extension filter to prevent certain file-types being uploaded in an ASP.NET HtmlInputFile control, using the RegularExpressionValidator.

Ultimately I came up with this
^([a-zA-Z]\:|\\)\\([^\\]+\\)*[^\/:*?<;>;|]+(\.txt|\.doc|\.xls|\.ppt|\.pdf|\.htm|\.html|\.zip)$
which seems to work OK, HOWEVER it's case-sensitive (so myFile.DoC won't upload).

If you want to only do validation on the server-side, a simple modification will enable 'case-insensitivity'
(?i)^([a-zA-Z]\:|\\)\\([^\\]+\\)*[^\/:*?<;>;|]+(\.txt|\.doc|\.xls|\.ppt|\.pdf|\.htm|\.html|\.zip)$

To really make it nice I wanted client-side validation as well, which meant building a custom validation control inheriting from BaseValidator. Most of the documentation I found was USELESS, but amazingly the 'beta' Longhorn documentation on Client-Side Functionality in a Server Control, Validator Control Samples and Client-Side Functionality in a Server Control
provided all the information I needed on the otherwise cryptic uses of RegisterValidatorCommonScript, RegisterValidatorDeclaration and the other bits and pieces required to implement a client- and server-side validator.

The result, InsensitiveRegularExpressionValidator is still in testing but will be posted soon.

Monday, 29 March 2004

Microsoft Word Automate WordCount frustration

It's amazing how difficult it can be to find information about MS Word when writing VBA (or automation using C#) - I needed to find the wordcount and charactercount programmatically, but the "document properties" m_Word.ActiveDocument.Words.Count and m_Word.ActiveDocument.Characters.Count don't seem to relate to ANYTHING!?!?!.

Thankfully this page myITforum.com : Document Word Counter gave me a push in the right direction:

object missing = System.Reflection.Missing.Value;
m_Word.ActiveDocument.ComputeStatistics(Microsoft.Office.Interop.Word.WdStatistic.wdStatisticWords, ref missing);

Why have Words and Characters properties if they don't DO anything???

P.S. Originally thought DSOFile would be able to do this job - but the Word and Char Count stored in the document properties in the filesystem isn't always up-to-date or meaningful either - it seems like ComputeStatistics is the only reliable way...

Wednesday, 24 March 2004

Parsing html markup text using MSHTML

Came across this article today; wondering whether this is a good solution for improving Searcharoo.net - both it's ability to "spider" the web by finding links in Html, and also parsing the Html into words for indexing (eg. pulling out the META tags, etc)... Parsing html markup text using MSHTML By Hendrik Swanepoel

I really want something lightweight that will help parse Html (a) links for spidering and (b) words for indexing... Other than some complex Regex, MSHTML is the only other option I've come across...

Sunday, 21 March 2004

WorldPay with C#/.NET [2]

Maybe useful info for those using C# with WorldPay...

WorldPay.COMcallbackClass callback = new WorldPay.COMcallbackClass(); 
callback.processCallback(); 
if (callback.hadError() ) { 
Trace.Write("WorldPay Error", callback.getRawAuthMessage() ) 
} 
if (callback.didTransSuc() ) { 

// #### WORLDPAY demo page #### 
string traceInfo = ""; 
traceInfo +="culture:" + Thread.CurrentThread.CurrentCulture.EnglishName + "\n"; 
traceInfo +="uiculture:" + Thread.CurrentThread.CurrentUICulture.EnglishName + "\n"; 

traceInfo +="getTransId:" + callback.getTransId() + "\n"; 
traceInfo +="getRawAuthMessage:" + callback.getRawAuthMessage() + "\n"; 
traceInfo +="getRawAuthCode:" + callback.getRawAuthCode() + "\n"; 
traceInfo +="getTransTime:" + callback.getTransTime() + "\n"; 
traceInfo +="shopperId:" + callback.shopperId + "\n"; 

traceInfo +="getInstallationId:" + callback.getInstallationId() + "\n"; 
traceInfo +="getCompanyName:" + callback.getCompanyName() + "\n"; 
traceInfo +="getAuthMode:" + callback.getAuthMode() + "\n"; 
traceInfo +="getAmount:" + callback.getAmount() + "\n"; 
traceInfo +="getCurrencyISOCode:" + callback.getCurrencyISOCode() + "\n"; 
traceInfo +="getAmountString:" + callback.getAmountString() + "\n"; 

traceInfo +="getDescription:" + callback.getDescription() + "\n"; 
traceInfo +="Customer getName:" + callback.getName() + "\n"; 
traceInfo +="Customer getAddress:" + callback.getAddress() + "\n"; 
traceInfo +="Customer getPostalCode:" + callback.getPostalCode() + "\n"; 
traceInfo +="Customer getCountryISOCode:" + callback.getCountryISOCode() + "\n"; 
traceInfo +="Customer getTelephone:" + callback.getTelephone() + "\n"; 
traceInfo +="Customer getFax:" + callback.getFax() + "\n"; 
traceInfo +="Customer getEmail:" + callback.getEmail() + "\n"; 

traceInfo +="\nTRANS_ID:" + callback.getParameterString("MC_DataID") + "\n";  // CUSTOM PARAM prefix with MC_ 
traceInfo +="MEMBER_ID:" + callback.getParameterString("MC_MemberID") + "\n"; // CUSTOM PARAM 

Trace.Write("WorldPay", traceInfo);

And if you're programming in Japanese... trying to get Japanese characters to display on the WorldPay pages was a real pain-- encoding issues (i'm using UTF-8 for my site - they use Shift_JIS). To set the job description, name and address info i'm currently using this hack

// so manually converting to HTML Unicode Entities 
// as discussed here http://www.randyrants.com/archives/000348.asp 
int ch1; 
string snail2="";  
foreach (char c in currentTransaction.Desc) { 
ch1 = c; 
if (!((ch1 >= 160) && (ch1 < 256))) {// if in 'ascii' range - *THINK* this works....
snail2 += c; // leave as-is 
} else { // convert to entity format Ӓ 
snail2 += string.Concat("&#", ch1.ToString(System.Globalization.NumberFormatInfo.InvariantInfo), ";"); 
} 
} 
purchase.setDescription (snail2); // HACK: HTML Entity Encoding 
[4-May-04] UPDATED CODE here

i18nguy.com

My job often involves multilingual programming (or at the very least content translation). This site i18nguy.com has an excellent collection of links on Internationalization (I18n), Localization (L10n), Standards etc. I've found it useful on a number of occasions, but sometimes I just go there to browse and learn.

Today's fav page is guidelines and resources.

Thursday, 18 March 2004

WayOffTopic: simple file exchange 'extranet'

Part of our everyday business is moving large files back and forth between offices (and countries). Usually FTP does the trick, but more and more clients are having firewall troubles with FTP (and to be honest, I'd rather not have the FTP service running at all).

What I wanted was a simple extranet application with an HTML File Upload and the ability to easily create client 'users' for one-off upload/downloads.

Google found Forms Authentication Using An XML Users File on MSDN, which has almost enough code to get me started.

First time in ages I've found an MS 'sample' useful (except the source projects on asp.net and winforms.net)!

Monday, 15 March 2004

SQL Server 2005 "Yukon" Full-Text Search

Interesting article about the 'internals' of the full-text-search in SQL Server, including discussion of ranking.

Data Access & Storage Home: SQL Server "Yukon" Full-Text Search: Internals and Enhancements (Microsoft SQL Server 9.0 Technical Articles)

First time I've seen "Yukon" officially named "SQLServer 2005"... maybe some useful ideas in there for Searcharoo

Sunday, 14 March 2004

Simple Site Search in .NET (1)

This article appeared on the MSDN Home Page recently: Part 4: Building a Better Binary Search Tree and it raised my curiousity about using the code provided to build a simple 'search engine' in C#.

I did a quick search to see what else was around (cheap/free products, or sample code) and found a few inspirational articles on WebMonkey, Perlfect and Apache.

A couple more articles on opening a file, removing white space and parsing HTML was all that was needed to get a basic search tool 'up and running' -- look for the code soon at Searcharoo.Net!

After being inspired by it, I didn't implement the Binary Search Tree straight away, that's another story...

WorldPay with C#/.NET

I'm in the process of converting an ecom site from ASP 'Classic' to .NET. The site uses the WorldPay gateway COM.

WorldPay supplies sample ASP files: purchase.asp and callback.asp. Purchase.asp "ported" just fine to C# (after using Reference -> Add Reference -> COM -> WorldPay Select COM to import the DLL).

BUT their callback.asp script failed with the following error

System.Runtime.InteropServices.COMException (0x80004005): aspcomp.AspComponentException: AspComponent: Retreiving MTx object context failed
at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData)
at WorldPay.COMcallbackClass.processCallback()
at ASP.callback_aspx.Page_Load(Object sender, EventArgs e) in C:\Inetpub\worldpay\callback.aspx:

on

WorldPay.COMcallbackClass callback = new WorldPay.COMcallbackClass();
callback.processCallback();

I couldn't figure out why one page would work and the other not. Surely it's the same DLL being imported/loaded? Changing the Identity in IIS, removing code, adding try/catch... nothing fixed the problem.

Just as I was about to give up and leave that one page in ASP 'Classic', I remembered long ago reading about a 'compability mode' in ASP.NET. A quick Google revealed this doco on aspcompat=true.

And it works! I could not find a single reference to this Googling for 'WorldPay' 'InterOp' 'Error' or any other likely terms -- surely I'm not the first!?!?

[hmm.. my first blog post...]