# Monday, October 26, 2009

Deleting Registry values with a .reg file

Sometimes you just want to do something simple in a batch file, and I'd always thought it wasn't possible to 'delete' a value in a .reg file (as the file is applied as a 'merge'), but it is...

Just replace the value with a - (minus)...

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer]
"NoDriveTypeAutoRun"=-



posted on Monday, October 26, 2009 10:16:01 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [1]
# Wednesday, June 10, 2009

Making Web Wireframes easier with Visio - The Pixel Dimensions Shape

Many people use Adobe Illustrator or Photoshop for designing Web layouts, but a lot of companies use Visio - especially for higer level 'wireframes'.  One of Visio's problems (<= 2003 anyway) is that it doesn't support pixel units.  This means you'll always be approximating the sizes of content areas and page elements, making the developer's job more difficult.

Enter 'Visio Guy', and the Pixel Unit Line Shape.  This is just a great addition for anyone using Visio.  Before you know it you'll be laying out pixel perfect shapes that the developers will then give you great pleasure in declaring 'this won't work in IE6!' :) 

A thing to note with Visio as well: If you're working with pretty small dimensions then the only way to get really precise is to zoom right in (like 500%), otherwise the whole 'snap/glue' thing will kick in based on the page units (probably mm), regardless of whether you've switched snap/glue off.

posted on Wednesday, June 10, 2009 9:49:48 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Wednesday, May 27, 2009

HOW TO Use SQL Server DTS Transform Data Task to export to dynamic Excel spreadsheet file

Many people (including me) are often still using SQL 2000 - which means that you might be using DTS (Data Transformation Services) to perform utility jobs on your databases. 
I've had a requirement to output data to Excel (not my choice - Excel usually isn't!) - which is subtly different to text output as CSV*.  Another requirement was to have a 'generated' file name in a known folder.  This is as simple as a date/time driven file name.  The 'job' runs once (or more) a day and so creates a new file each time.  Easy right?

Well - it turns out that if you go through the normal motions of creating an Excel connection and using a Transform Data Task it's likely to work first time and leave you thinking 'great'.  Unfortunately when you run it again you're likely to get a message similar to the following:

Table 'New_Table$' not found. 

After much digging (hence the reason for writing this), I discovered that in order to map the columns successfully you basically have to create the table in the spreadsheet before running the transformation (the 'create table' part of creating the transformation).  If you've got a dynamic file name then the next time you'll  effectively have a blank spreadsheet without the required receiving 'table', and it will fail, unless you've recreated the sheet before the transformation.

OK so assuming you want to output some data to a spreadsheet with a dynamic filename with a Transform Data task then here's how you do it.

  1. I put all files in the same folder so I use 2 global variables to build the filename - NetworkLocation and FinalLocation
    1. First, Right-click on a blank part of your package screen and choose Package Properties, then Global Variables tab.
    2. Create the two global variables NetworkLocation and FinalLocation
    3. Edit the NetworkLocation value to be the folder (including a final '\') of where you want to store the files.
    4. Leave FinalLocation blank (we'll create that in the next step.
  2. Add an ActiveX Script Task with the following code (which you can obviously tailor the name to your needs).  It basically creates a date/time based file name, and stores a concatenation of the network location and file name in the FinalLocation global variable.

    '**********************************************************************
    '  Visual Basic ActiveX Script
    '************************************************************************
    Function Main()

        Dim fileName
        fileName = "MyOutput_" & DatePart("yyyy",Date) & Right("0" & DatePart("m",Date), 2) & Right("0" & DatePart("d",Date), 2) & "_" & Right("0" & DatePart("h",Now), 2) & Right("0" & DatePart("n",Now), 2) & Right("0" & DatePart("s",Now), 2)
        DTSGlobalVariables("FinalLocation").Value = DTSGlobalVariables("NetworkLocation").Value + fileName + ".xls"

        Main = DTSTaskExecResult_Success

    End Function
  3. Add your data connections
    1. Create your source connection (presumably SQL Server or wherever your data's coming from)
    2. Create your destination connection (A Microsoft Excel 97-2000 connection) and set an initial file name (best to be in the folder specified in NetworkLocation)
  4. Create a Data Transformation Task connecting your source and destination connections as per normal
    1. Add in your Table/View or SQL Query to define the source data.
    2. In the Destination tab click 'Create...' next to 'Table name:' (a dialog will pop up 'Create Destination Table').  Change the table name (or column definitions) if you like, then COPY THE ENTIRE TEXT - and paste into a text editor for safe keeping.
    3. Click OK (This will create a new sheet in the spreadsheet you've specified in your connection, ready to receive your data).  Your fields should then also be populated in the table below.
    4. Check the transformations tab - you should get a nice one to one mapping between source and destination columns.
  5. Create a Dynamic Properties Task (Here's where it all comes together)
    1. Click 'Add...' to create a new property.  Choose your Excel Connection and pick the 'DataSource' property, then click 'Set...'
    2. Choose 'Global Variable' as the source and FinalLocation as the variable - Click OK.
    3. Click 'Add...' to create another property.  Choose the DTSTask_DTSDataPumpTask_? in the Tasks node (your data transformation task) and pick the 'DestinationObjectName' property.
    4. Choose 'Query' as the source and paste in your 'CREATE TABLE' statement from earlier - Parse to check, then click OK.
  6. Create the Workflow for the task
    1. Select the Dynamic Properties Task, right-click and select Workflow --> Workflow Properties.
    2. Click 'New' and set the ActiveX Script Task as 'Source Step'.
    3. Select the Data Transformation Task, right-click and choose Workflow Properties.
    4. Click 'New' and set the Dynamic Properties Task as 'Source Step'.
That's basically it - the package should look something like this:



It should also now do the following (in order):
  1. Set filenames (stored in global variables)
  2. Set filename of the excel connection, create the destination 'sheet' from the global variables and the query in the transformation
  3. Do the transformation and store the results in a date/time named Excel spreadsheet in your configured folder

If you ever need to change the query (columns) then you'll need to regenerate the 'CREATE TABLE' statement and reflect it in the Dynamic Properties Task (5.c)


*Typically an Excel export covers your back with datatypes where a CSV export won't - so when you open in Excel you'll have to format columns specifically yourself - like converting dates, and also long numbers from scientific format to text format.
posted on Wednesday, May 27, 2009 9:34:47 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Thursday, April 23, 2009

How to generate a list of Visual Studio Shortcuts

I'd seen addins that list Visual Studio shortcuts before, but seem to have lost them.  I also used to use ReSharper, so memorised its shortcuts.  I'm now back to 'naked' Visual Studio, so am having to re-learn the standard shortcuts.

I was surprised to find this article on MSDN that has code for a macro to produce a html page of all the shortcuts.  Just run the macro, open it up and search in the browser whenever you're having problems remembering - unless you really want to print it (in which case I'd be inclined to bang a bit of CSS in there :) ).  I might improve upon the whole situation if I find myself needing to refer to it more often, and will post updates here.

posted on Thursday, April 23, 2009 8:44:07 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [1]
# Monday, December 08, 2008

How to force Commit Comments with SubVersion (on Windows)

We have some newer SVN users at the moment, and so to 'help' them with good practice I found this rather simple pre-commit hook to disallow commits with no comment.

Found it here - just make sure you click the 'view plain' link at the top of the script otherwise you'll get line numbers when you copy.


posted on Monday, December 08, 2008 12:04:35 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Thursday, December 04, 2008

Removing the scrollbar Page Shift from Firefox

This had bugged me for a while.  A lot of sites (including some of the ones I develop) tend to have a fixed width layout these days and some browsers (IE particularly) 'always' has a visible scrollbar.  This means that the available screen width is constant whether the page scrolls or not. 

Firefox on the other hand (and Chrome/Opera/Safari) seem to have this off by default.  This of course seems reasonable until you have a fixed width, centred layout that 'shifts' when you switch from a non-scrolling to a scrolling page.  It's just a bit off-putting.

Fortunately Firefox is configurable and the following will fix that up for you. (I'm sure the other browsers are capable of something similar but I'm not using them much :) )

  1. Find your profile directory (it's bound to be the 'default' one unless you're developing Firefox addons.  You'll normally find it in c:\documents and settings\username\application data\Mozilla\Profiles\xxxxx.default\
  2. Go to the 'chrome' subfolder and create a file called userContent.css (you'll probably find there's a couple of 'example' files there already.
  3. Add the following (Firefox-specfic) line to the file:

    html { overflow: -moz-scrollbars-vertical !important; }

  4. Save the file, exit Firefox and start her up again.  You should now have a permanent scrollbar which eliminates the page shift. 

posted on Thursday, December 04, 2008 10:51:55 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Wednesday, November 26, 2008

Create SQL Server User and Role Script to execute Stored Procedures Only

I really thought I'd posted this before, but here's a script I use quite often when I need to create a SQL user that needs to just have 'execute' access to Stored Procs. 

This is usually used for web apps where the server is going through as a single user.  I'd encourage anyone to ensure that you always start off with granting the minimum access required and only add more when actually needed.  If you've got different roles in the application (viewer, administrator etc) then create different roles, with a user for each.

The intention is to remove the need to give direct table access (as we only call Stored Procs right?).  The script doesn't actually do the 'grants' for you, but will generate another batch that you can execute if you like.  I prefer to put the grants in the script for each proc/function anyway, just so it's a little easier to maintain, and you can specifically grant access to the 'admin' procs to the 'admin' role, whilst limiting any 'normal' users to the read-only/everyday ones. 

There's satisfaction to be had from an app that connects to the database via an appropriate user, which is in a role that can only do what is necessary for the role, and can't access tables directly.  Not only is it neat, it's also (relatively) secure.

Here's the script...




-- MSIMNER: This script will generate a role and user for SQL Access to a single database
-- The role will have no specific access to read or write tables but will be granted access
-- through execution of stored procedures ONLY

USE <databasename, sysname, MyDB>

DECLARE @UserName VARCHAR(20)
DECLARE @Password VARCHAR(20)
DECLARE @DBName   VARCHAR(20)
DECLARE @Role     VARCHAR(20)

--SET PASSWORD HERE
SET @Password 'ixFg2tfk'

SET @UserName '<databasename, sysname, MyDB>User'
SET @Role     'db_exec_<databasename, sysname, MyDB>'
SET @DBName   '<databasename, sysname, MyDB>'

IF EXISTS(SELECT FROM master.dbo.syslogins WHERE name = @UserName)
BEGIN
    exec 
sp_revokedbaccess @name_in_db @UserName
    
exec sp_droplogin @loginame @UserName
    
exec sp_droprole @rolename @Role

END

--Create User
exec sp_addlogin @loginame @UserName, @passwd @Password
exec sp_grantdbaccess @loginame @UserName
exec sp_defaultdb @loginame @UserName, @defdb @DBName
exec sp_addrole @rolename @Role, @ownername 'dbo'
exec sp_addrolemember @rolename @Role, @membername @UserName


-- The output of this command will need to be run separately - i.e. paste the output into a new window and run... 

--SELECT 'GRANT EXECUTE ON ' + name + ' TO ' + @Role , *
--FROM sysobjects
--WHERE type = 'P'

Colorized by: CarlosAg.CodeColorizer
posted on Wednesday, November 26, 2008 10:10:59 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Thursday, November 20, 2008

Minify your Javascript using JSMin to minimise client downloads

This isn't a new thing, but I just came across a C# console app class, ported from JSMin to help with 'minifying' (or uglifying) your Javascript.  You can find it here.

Just create a new C# console app, pass in the 'input file' and 'output file' and away you go...

posted on Thursday, November 20, 2008 11:16:10 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Saturday, October 18, 2008

Updated DeletedOld CodeProject Article

After an extremely long time I've made an update to the DeleteOld console app and updated the CodeProject article.  The original was .NET 1.1 - this one's now 2.0 (I'm not on 3 yet!)

It still does the same things it did - deletes files of a specified age, but now also:
  • Allows files to be deleted 'newer' than a certain age as well as 'older'
  • Allows files to be deleted based on an absolute date (overriding the timeframe arguments)
  • Allows the 'root' path to be preserved if 'remove empty folders' is selected and the path specified is empty.
  • Fixed a bug in Arguments parsing regex as noted by dudik
  • Changed output datetime format to 'full' rather than specific dd/mm/yyyy as noted by a.plus.01
I had a need for the absolute date, so thought I may as well cover off a few other things. 

Hope it's of some use...

You can also see my other CodeProject articles.

posted on Saturday, October 18, 2008 8:32:58 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Thursday, October 09, 2008

Serving SSL from localhost on IIS 5 on Windows XP using Self-Signed certificates

I've probably glossed past this a few times, but it turns out getting SSL working on your dev machine is less painful than you'd think...

  1. install iis60rkt.exe from http://www.microsoft.com/downloads/details.aspx?FamilyID=56fc92ee-a71a-4c73-b628-ade629c89499&DisplayLang=en on your local machine. It's intended for IIS 6.0 but it works in IIS 5.1.
  2. Select custom install if you only want to use SelfSSL. Otherwise, install all.There's a lot of other useful stuff there.
  3. Select Programs --> IIS Resources --> SelfSSL --> SelfSSL command prompt
  4. In the command prompt, you'll see a list of all possible command parameters..
    1. Type 'selfssl.exe /V:60'     This indicates the certificate is valid for 60 days
    2. Type Y to answer the question "Do you want to replace the SSL settings for site 1 (default website) ?
    3. Type 'iisreset' to restart IIS.
If your browser prompts something like 'Certification Authority not recognised' (in Firefox), just put the url in the exception list or override the warnings when viewing the page.

Thanks to Ricky for getting this info together.

posted on Thursday, October 09, 2008 9:56:33 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Monday, September 15, 2008

Replacing Notepad with Notepad2

Notepad2 is great - no arguments.  It was only this morning I actually got around to 'replacing' the standard notepad.exe with notepad2.exe.  You'd think it was just a simple rename, but Omar Shahine shows the way...

posted on Monday, September 15, 2008 8:36:04 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Wednesday, August 06, 2008

Setting up a Continuous Integration .NET Build Server without Visual Studio

We start with Windows XP SP2 (doesn't really need to be a 'server').  My requirement is to rely on the .NET framework and open source tools - i.e. not requiring a Visual Studio Licence.  We're using .NET 2.0 so everything will be in reference to that.

I assume here that you'll be familiar with the actual software below and just want to get a Build Server up and running without having to install Visual Studio.  If you're not familiar with Continuous Integration then start by looking at Martin Fowler's Continuous Integration article and then the info on CruiseControl.NET, as that's the tool that pulls everything together. 

There's help on each of the sites below for installing and using each of the tools, so I won't go into detail about each one.  The order of the list isn't critical, but you'll probably have some issues unless all are done.

  1. Install IIS from Add/Remove Windows Components (used for CruiseControl.NET) if not already present
  2. Open up a command prompt and run C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis.exe -i (to install ASP.NET in IIS)
  3. Install CruiseControl.NET (1.4) from http://confluence.public.thoughtworks.org/display/CCNET/Download.  NOTE:  If you're using a remote SubVersion repository (as in my case) then you'll need to run the CruiseControl.NET Service as a domain user that has access to the network location of the repository  rather than LocalSystem.  This is because CruiseControl will be using the SVN client to poll for changes. 
  4. Install Microsoft .NET Framework 2.0 SP1 from http://www.microsoft.com/downloads/details.aspx?familyid=029196ed-04eb-471e-8a99-3c61d19a4c5a&displaylang=en
  5. Install latest SubVersion (1.5) from http://www.collab.net/downloads/subversion/
  6. Install appropriate NUnit from http://nunit.org/index.php?p=download (targeting .NET framework 2.0 - I have 2.4.6)
  7. Install latest FxCop from http://www.microsoft.com/downloads/details.aspx?familyid=3389F7E4-0E55-4A4D-BC74-4AEABB17997B
  8. Install .NET Framework 2.0 SDK from http://www.microsoft.com/downloads/details.aspx?familyid=fe6f2099-b7b4-4f47-a244-c96d69c35dec&displaylang=en (you'll need this to get around a NAnt bug whereby it can't resolve an internal property to load the .NET framework)
  9. Download latest NAnt from http://nant.sourceforge.net/
  10. Download latest NAntContrib from http://nantcontrib.sourceforge.net/ (You'll need this to run MSBuild)
  11. To save getting errors when building Web Projects (and other types) the easiest thing is to copy c:\program files\msbuild from your dev machine to the build server (otherwise you'll have to alter the path of where the targets are in each project).

Once you've got all of these set up then you'll be able to add some builds to your ccnet.config file ( most likely c:\program files\CruiseControl.NET\server\ccnet.config)

Test CruiseControl's happy by going to http://localhost/ccnet It should come up OK but basically show 'no projects'.

I'm not going to go into detail about setting up the builds here but may cover that in a follow up article, as it requires some more setup with SVN and a 'standard' project structure (to get benefit from reuse of build scripts).  If you're still with me then I'll share one last thing which might help... 

Some people choose to have NAnt in a standard place and just reference it from the PATH, but I now use the power of svn:externals to drag in NAnt, NUnit and other common external dependencies like MS Enterprise Library from a shared SVN location to each project.  This means you just get latest of a project and it has 'everything' needed to build - no installations or assumptions about locations of tools.

posted on Wednesday, August 06, 2008 8:35:10 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Wednesday, July 30, 2008

Microsoft Guidance Explorer - An alternative to WikidPad?

I tried Guidance Explorer again after a good while (for the purposes of documenting standards and processes), and concluded that it wasn't going to be up to the job because printing wasn't supported. 

I've been using WikidPad for a while and have been struggling with it's usability so I had another google around the printing capability of Guidance Explorer (my eventual medium is likely to be some other wiki-like product, so this is likely to just be my 'input medium').  I found the (rather obvious) Guidance Explorer Overview on Codeplex that explains that printing is indirectly supported through the export formats. I tried it and - whoa!! I take it all back. 

The export to Word format at different levels and different views is just great.  It gives you an index at the top with links to the actual content.  The formatting's nice too.  The HTML format's just the same as the Word doc visually, but spits out the content to separate files which is a bit nicer for deployment.

I still can't believe how little-known Guidance Explorer is, but much of the Patterns and Practices work is documented, along with many other resources.  I'll certainly be saying Bye bye to WikidPad.


posted on Wednesday, July 30, 2008 10:14:58 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Wednesday, July 16, 2008

NAnt Solution Task bug - Error checking whether 'xxx' is an enterprise template project.

Just spent a little while looking into this.  I thought I'd be clever and add my build script to the solution it builds so I can get some intellisense from Resharper (although it's somewhat limited).  This added the file into 'Solution Items' which is a virtual folder.

The error appears because NAnt expects to find valid project files in the solution folders.  I had the same error previously with Web Site Projects (as these are also 'special folders'). 

I'm using NAnt 0.86 (Build 0.86.2898.0; beta1; 8/12/2007), and from the bug report it seems that it's fixed in 0.86 beta 2 (not yet out) so you'll have to get a nightly build if you're desperate.

My workaround for the moment is to use the NAntContrib MSBuild task to build my Web Deployment project (which in turn builds all the dependencies), as follows:

<target name="build.webdeploy" description="Compiles a Web Deployment Project using MSBuild">
  <msbuild project="${project.dir}\mydeploydir\mydeployproj.wdproj" verbosity="Detailed" >
     <arg value="/p:Configuration=${build.configuration}" />
  </msbuild>
</target>


You can fill in the blanks yourself with the parameters.

posted on Wednesday, July 16, 2008 1:00:04 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Wednesday, July 09, 2008

Test out String.Format options online

Love this little tool.  It's making me think about writing some stuff for Silverlight!

I just plain can't remember the options for String.Format and standard/custom strings so being able to try them out is great.  Nice work!

I'd normally use John Sheehan's excellent Cheat Sheet, but I'll probably stick to the tool in future.

posted on Wednesday, July 09, 2008 5:13:49 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [1]

Who needs Internet Explorer when you can use it in Firefox?

I love Firefox and its addons, although I still habitually use Internet Explorer.  IE6 at work (which always provides some nice CSS challenges!) and IE7 at home.

I'm a bit slow off the mark with using firefox, but the latest addon that someone sent me yesterday may have tipped the balance.

The IE Tab addon actually uses the IE rendering engine to render in a new tab in Firefox.  It's quite amusing when you right-click and get the IE context menu.

The really great thing about this is that you can then use the power of the other Firefox addons to develop for IE.  Here's my list of 'must haves'.

  • Firebug.  This is quite simply the best addon.  Hit F12 and you can instantly see (and manipulate) all sorts of things in the comfort of your own browser.
  • YSlow.  Great Javascript profiler
  • FireCookie. See and change your cookies (for development purposes of course! - Good to test the security of your apps)
  • Web Developer.  Bit of overlap with Firebug but some cool features.

The IE Developer Toolbar is still a pretty good tool, but Firebug just gives you more info when you're debugging CSS (which is inevitable with IE).

 

posted on Wednesday, July 09, 2008 12:24:40 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Tuesday, July 08, 2008

Search SQL Server Stored Procedures for any text string

Here's the SQL script I probably use most when I'm trying to work out dependencies and scope of development changes.  Query Analyzer allows you to search objects (hit F4 - it's cool!) and find objects by 'name', but there's no obvious way to search stored procs for a particular string.

SQL Server hides Stored Procedure code in the sysComments table (in each database), and so it's possible to search.  This is another script I found from not sure where (so can't really take credit).  It does a great job of scanning and even gives you line numbers.  It would be great to be able to just shove this into the master database, but you need to be in the context of the database you're searching in (or it would have to be amended somewhat to run some more 'dynamic' SQL). 

I have previously front-ended this for development databases with an ASP.NET page that simply runs the query substituting a parameter for the search text and connecting to the correct database.  The results show nicely in a grid.

Here's the code - just paste into a new query in your chosen database, change @SearchTerm and away you go...

 

SET NOCOUNT ON
GO

declare 
@dbname sysname
,@SearchTerm    varchar(100)
,@BlankSpaceAdded   
int
,@BasePos       int
,@CurrentPos    int
,@TextLength    int
,@LineId        int
,@AddOnLen      int
,@LFCR          int --lengths of line feed carriage return
,@DefinedLength int
,@obj_name    varchar(255)
,@prev_obj_name 
varchar(255)


/* NOTE: Length of @SyscomText is 4000 to replace the length of
** text column in syscomments.
** lengths on @Line, #CommentText Text column and
** value for @DefinedLength are all 255. These need to all have
** the same values. 255 was selected in order for the max length
** display using down level clients
*/
,@SyscomText    nvarchar(4000)
,@Line          
nvarchar(255)

set @SearchTerm 'MyTable'
set @dbname db_name()


set @SearchTerm '%' + @SearchTerm + '%'

Select @DefinedLength 255
SELECT @BlankSpaceAdded /*Keeps track of blank spaces at end of lines. Note Len function ignores
                             trailing blank spaces*/
CREATE TABLE #CommentText
(obj_name 
varchar(255)
 ,LineId    
int
 
,Text  nvarchar(255collate database_default)

/*
**  Make sure the @objname is local to the current database.
*/
if @dbname is not null and @dbname <> db_name()
        
begin
                raiserror
(15250,-1,-1)
        
end

    begin

        DECLARE 
ms_crs_syscom  CURSOR LOCAL
        FOR SELECT 
so.name, sc.text FROM syscomments sc
    
inner join sysobjects so
    
on so.id = sc.id
    where 
so.type 'P'
    
AND sc.encrypted 0
                
ORDER BY number, colid

        
FOR READ ONLY
    
end

/*
**  Get the text.
*/
SELECT @LFCR 2
SELECT @LineId 1


OPEN ms_crs_syscom

FETCH NEXT FROM ms_crs_syscom into @obj_name, @SyscomText

WHILE @@fetch_status >0
BEGIN

    SELECT  
@BasePos    1
    
SELECT  @CurrentPos 1
    
SELECT  @TextLength = LEN(@SyscomText)

    
IF @prev_obj_name <> @obj_name
        
--start again
        
SELECT @LineId 1
    
    
WHILE @CurrentPos  !0
    
BEGIN
        
--Looking for end of line followed by carriage return
        
SELECT @CurrentPos =   CHARINDEX(char(13)+char(10), @SyscomText, @BasePos)

        
--If carriage return found
        
IF @CurrentPos !0
        
BEGIN
            
/*If new value for @Lines length will be > then the
            **set length then insert current contents of @line
            **and proceed.
            */
            
While (isnull(LEN(@Line),0) + @BlankSpaceAdded + @CurrentPos-@BasePos + @LFCR) > @DefinedLength
            
BEGIN
                SELECT 
@AddOnLen @DefinedLength-(isnull(LEN(@Line),0) + @BlankSpaceAdded)
                
INSERT #CommentText VALUES
                
( @obj_name,
          @LineId,
                  
isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @AddOnLen), N''))
                
SELECT @Line = NULL, @LineId @LineId + 1,
                       @BasePos 
@BasePos + @AddOnLen, @BlankSpaceAdded 0
            
END
            SELECT 
@Line    = isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @CurrentPos-@BasePos + @LFCR), N'')
            
SELECT @BasePos @CurrentPos+2
            
INSERT #CommentText VALUES( @obj_name, @LineId, @Line )
            
SELECT @LineId @LineId + 1
            
SELECT @Line = NULL
        END
        ELSE
        
--else carriage return not found
        
BEGIN
            IF 
@BasePos <@TextLength
            
BEGIN
                
/*If new value for @Lines length will be > then the
                **defined length
                */
                
While (isnull(LEN(@Line),0) + @BlankSpaceAdded + @TextLength-@BasePos+1 ) > @DefinedLength
                
BEGIN
                    SELECT 
@AddOnLen @DefinedLength - (isnull(LEN(@Line),0) + @BlankSpaceAdded)
                    
INSERT #CommentText VALUES
                    
( @obj_name,
              @LineId,
                      
isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @AddOnLen), N''))
                    
SELECT @Line = NULL, @LineId @LineId + 1,
                        @BasePos 
@BasePos + @AddOnLen, @BlankSpaceAdded 0
                
END
                SELECT 
@Line = isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @TextLength-@BasePos+1 ), N'')
                
if LEN(@Line) < @DefinedLength and charindex(' ', @SyscomText, @TextLength+1 ) > 0
                
BEGIN
                    SELECT 
@Line @Line + ' ', @BlankSpaceAdded 1
                
END
            END
        END
    END
    
--Save previous name
    
SET @prev_obj_name @obj_name
    
FETCH NEXT FROM ms_crs_syscom into @obj_name, @SyscomText
END

IF 
@Line is NOT NULL
    INSERT #
CommentText VALUES( @obj_name, @LineId, @Line )

select from #CommentText 
where  patindex(@SearchTerm, text) <> 0

CLOSE  ms_crs_syscom
DEALLOCATE     ms_crs_syscom

DROP TABLE     #CommentText


Colorized by: CarlosAg.CodeColorizer
posted on Tuesday, July 08, 2008 9:38:31 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]

Generate SQL Inserts from existing data

This is one from the archives that obviously got away.  I had need for this today so thought I'd post so I don't have to look so hard next time!

It's pretty much a case of create the SP in your chosen database and run with a tablename as the parameter.  I originally found this on one of the SQL sites (probably SQLServerCentral), and amended it slightly to add some basic filtering in the @where parameter.

There's a few limitations from memory - with blob, image and text fields, but for your average tables this will probably do the trick just fine.

I've also used a different method that basically builds up a single INSERT with SELECT 'literal values' UNION SELECT 'literal values'.  That works fine and is potentially easier to control transaction-wise as everthing's in a single statement.  Anyway here's the code....

if exists (select from dbo.sysobjects where id = object_id(N'[dbo].[usp_InsertGenerator]'and OBJECTPROPERTY(idN'IsProcedure'1)
drop procedure [dbo].[usp_InsertGenerator]
GO


CREATE PROC 
dbo.usp_InsertGenerator
(@tableName 
varchar(100), @where varchar(1000= NULLas
 
--Declare a cursor to retrieve column specific information for the specified table
DECLARE cursCol CURSOR FAST_FORWARD FOR 
SELECT 
column_name,data_type FROM information_schema.columns WHERE table_name @tableName
OPEN cursCol
DECLARE @string nvarchar(3000--for storing the first half of INSERT statement
DECLARE @stringData nvarchar(3000--for storing the data (VALUES) related statement
DECLARE @dataType nvarchar(1000--data types returned for respective columns
SET @string='INSERT '+@tableName+'('
SET @stringData=''
 
DECLARE @colName nvarchar(50)
 
FETCH NEXT FROM cursCol INTO @colName,@dataType
 
IF @@fetch_status<>0
 
begin
 print 
'Table '+@tableName+' not found, processing skipped.'
 
close curscol
 
deallocate curscol
 
return
END
 
WHILE 
@@FETCH_STATUS=0
BEGIN
IF 
@dataType in ('varchar','char','nchar','nvarchar')
BEGIN
 SET 
@stringData=@stringData+''''+'''+isnull('''''+'''''+'+@colName+'+'''''+''''',''NULL'')+'',''+'
END
ELSE
if 
@dataType in ('text','ntext'--if the datatype is text or something else 
BEGIN
 SET 
@stringData=@stringData+'''''''''+isnull(cast('+@colName+' as varchar(2000)),'''')+'''''',''+'
END
ELSE
IF 
@dataType 'money' --because money doesn't get converted from varchar implicitly
BEGIN
 SET 
@stringData=@stringData+'''convert(money,''''''+isnull(cast('+@colName+' as varchar(200)),''0.0000'')+''''''),''+'
END
ELSE 
IF 
@dataType='datetime'
BEGIN
 SET 
@stringData=@stringData+'''convert(datetime,'+'''+isnull('''''+'''''+convert(varchar(200),'+@colName+',121)+'''''+''''',''NULL'')+'',121),''+'
END
ELSE 
IF 
@dataType='image' 
BEGIN
 SET 
@stringData=@stringData+'''''''''+isnull(cast(convert(varbinary,'+@colName+') as varchar(6)),''0'')+'''''',''+'
END
ELSE 
--presuming the data type is int,bit,numeric,decimal 
BEGIN
 SET 
@stringData=@stringData+''''+'''+isnull('''''+'''''+convert(varchar(200),'+@colName+')+'''''+''''',''NULL'')+'',''+'
END
 
SET 
@string=@string+@colName+','
 
FETCH NEXT FROM cursCol INTO @colName,@dataType
END
DECLARE 
@Query nvarchar(4000)
 
SET @query ='SELECT '''+substring(@string,0,len(@string)) + ') VALUES(''+ ' substring(@stringData,0,len(@stringData)-2)+'''+'')'' FROM '+@tableName
IF @where IS NOT NULL
 SET 
@query @query + ' where ' + @where
 
exec sp_executesql @query
 
CLOSE cursCol
DEALLOCATE cursCol
GO

Colorized by: CarlosAg.CodeColorizer
posted on Tuesday, July 08, 2008 9:18:47 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Monday, June 30, 2008

Del.icio.us - FavPal.NET is dead?

This sounds pretty unlikely, but I'm a new (and very enthusiastic) del.icio.us user (and have to think hard about where to put the dots every time I type it).  I guess you can't jump on 'every' bandwagon.  I tend to discover stuff that I 'need' these days rather than get too 'wowed' by yet another networking site.

I developed a tool called FavPal.NET (don't worry - I'm sure you haven't heard of it!), a few years back as I saw there was nothing that allowed you to search through browser bookmarks with any degree of speed or accuracy.  It was a tray app that kept a 'cache' pool of IE instances (as load time was pretty bad back then), and allowed you to search through your favourites, then load up URL's into a cached instance in double-quick time.  This served two purposes

  1. Search Favourites
  2. Load IE quickly

Del.icio.us obviously more than scores with requirement 1 as it allows you to keep your bookmarks centrally (BTW - I also use the IGoogle bookmarks gadget for 'home page' access to my most frequently used stuff).  One thing was still lacking - an 'integrated' search within the browser (or rather a search without having to point your browser to del.icio.us), but now there's an IE extension (for IE6/7) that sits as a sidebar (nice).

I noticed there's a Delicious.NET framework, and I'm still not terribly satisfied with the initial application load speed of any browser (even Firefox and Safari), so maybe FavPal's not quite dead.  The search functionality could now simply hook into del.icio.us and still use the cache.  With tabbed browsing on IE7 now though the object model may well have changed.  If anyone wants to have a crack at it you're more than welcome, and I'll send you the code, as it got removed from the late lamented gotdotnet workspaces.

Of course the load speed will ineviatably go up with the more plugins like del.icio.us you bloat the browser with - d'oh!

posted on Monday, June 30, 2008 8:24:27 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Wednesday, June 11, 2008

Giving Guidance Explorer another go

I downloaded the Patterns and Practices 'Guidance Explorer' a year or more ago, and quite liked the idea.  I think the problem with any tool that tried to be a flexible storage and delivery method for 'organic' content (I'm interested in using it for development processes/standards etc) is viewed and searched by different people in completely different ways. 

Some like to dip into 'reference' material, some always 'search', and some people like to print everything and read cover to cover.  I know there've been some updates to the tool, and although the download count is still pretty low (3000 ish) I'll give it another go and see if it works for me.

Other people just throw this stuff into docs and put it on SharePoint.  Others use something like WikidPad - which again I liked but ultimately got out of the habit of using - meaning it obviously didn't work for me. 

Update

I just noticed that there's no possible way of printing from Guidance Explorer, and so while I can understand the reasons why it isn't implemented (to try and encourage people to write screen-consumable, concise content), it simply won't work for the users I'm looking to target.  Shame :-(

posted on Wednesday, June 11, 2008 9:02:04 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Monday, June 02, 2008

Using RSS.NET to re-write an existing feed XML file

Don't know what happened to RSS.NET.  Looks like it's trying to go commercial, but still very quiet.  Code examples are few and far between (with some simple ones on the site).  Also on InformIT.

I'm writing a quick RSS console app (as I lost the last one I wrote!) that I can use to write an RSS feed from a SubVersion hook.  Most people use a pre-existing tool for Python (can't remember the name), but I thought I'd give RSS.NET another go just for kicks.

The example above works fine assuming you're maintaining state somewhere other than the feed xml file itself.  The example also assumes you're serving this up to the web.  You can override the RssFeed.Write() method to take a filename.

If run the same code again (assuming you've written to a file) it will simply overwrite it with one item (not add to it).  This isn't what I wanted so...

You need to

  1. Read the file back in if it's there - otherwise create
  2. Add your item to the existing channel if it's there - otherwise create
  3. Fix the date behaviour as RSS.NET always assumes UTC dates and appends 'GMT'.  The problem here is that if you're in Australia (like me) reading and rewriting the same items will effectively add several hours on to existing items every time, because you write the date you read back in for existing items (read and parse into region-specific date, then write back as is).  There's two ways to fix this:
    1. Before you add your new item - loop through all items and change the item.PubDate to item.PubDate.ToUniversalTime().  This effectively sets it back to the 'correct' date.
    2. Change the RSSWriter class in RSS.NET to convert ToUniversalTime for the Item.PubDate, Channel.PubDate etc.  This seems like a better option, but it has potentially more knock on effects in RSS.NET.  I'm here to achieve a result, not change the behaviour (possibly adversely) of RSS.NET so I chose option 1

So here's the code.  Not finished yet and rough around the edges, but works as I need.  The intention is to avoid the need for config files and configuring up of feeds specifically.  I just want a library function that's called by a console app.  The web serving will simply be based on the location of the file and pointing to some folder in IIS.

        private static void WriteFeed(string feedFileName, string feedName, string feedDescription, 
        string feedURL, string itemTitle, string itemDescription, 
        DateTime itemPublishDate, 
string itemURL)
        {
            
bool newFeed = false;
            
//Try and first open the feed (to see if it's existing)
            
RssFeed feed = null;
            try
            
{
                feed 
RssFeed.Read(feedFileName);
            
}
            
catch (FileNotFoundException ex)
            {
                feed 
= new RssFeed();
                
newFeed = true;
            
}
            
catch (Exception ex)
            {
                WriteError(ex)
;
                return;
            
}

            RssChannel channel 
= null;

            
//Loop through all channels and if we've got the same title reuse
            
for (int 0i < feed.Channels.Counti++)
            {
                
if (feed.Channels[i].Title == feedName)
                {
                    channel 
feed.Channels[i];
                    break;
                
}
            }

            
if (channel == null)
            {
                channel 
= new RssChannel();
                
feed.Channels.Add(channel)//might blow up if already there?
            
}

            RssItem item 
= new RssItem();

            
item.Title itemTitle;
            
item.Description itemDescription;
            
item.PubDate itemPublishDate.ToUniversalTime();
            
item.Link = new Uri(itemURL);

            
//To ensure we don't screw up existing dates - convert to UTC
            
foreach (RssItem existingItem in channel.Items)
            {
                existingItem.PubDate 
existingItem.PubDate.ToUniversalTime();
            
}

            
//Now add our new item
            
channel.Items.Add(item);

            
channel.Title feedName;
            
channel.Description feedDescription;
            
//channel.LastBuildDate = channel.Items.LatestPubDate();
            
channel.PubDate DateTime.UtcNow;
            
channel.Link = new Uri(feedURL);

            
feed.Write(feedFileName);
        
}

Colorized by: CarlosAg.CodeColorizer

 

 

posted on Monday, June 02, 2008 9:28:25 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Thursday, May 22, 2008

Getting the most out of ASP.NET Web Deployment Projects

In my ongoing love (but mostly) hate relationship with ASP.NET Web 'Site's' I've been using Web Deployment projects to make things more bearable. 

I currently swap in connection strings from 3 files - one for each build configuration (debug, test, release - connectionstrings.debug.config etc ).  This works fine as per the doco on WDP.  I use CruiseControl.NET and NAnt to automate builds, and a few nagging 'automated' pieces were missing from the puzzle. 

  1. Encryption of connectionStrings (or other web.config sections that you want to protect) - without affecting the 'source' file.  I've assumed here that 'internal' people are trusted. 
  2. Changing of other config stuff (like debug=false) in the test and release builds.  (My attempts to get this to work had previously failed as you don't seem to be able to specify system.web as a replaceable section.
  3. Encrypting Forms authentication passwords, using MD5 hash.  This isn't difficult, I just didn't have a tool to generate the hash value.

Encryption of config sections

OK - after re-reading Scott Gu's post on Web Deployment Projects, and K. Scott Allen's post on how to simply encrypt sections of config files, I realised that I could just add a post-build event (manually) in the wdproj file (right-click in solution explorer --> open project file). 

The build events are already in but commented at the bottom of the file.  I ended up with

<Target Name="AfterBuild">
<Exec WorkingDirectory="$(OutputPath)" 
Command="C:\windows\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis -pef connectionStrings ."
/>
<!-- Also remove our 'source' config files using the del command rather than the delete task as you
have to jump through hoops to specify wildcards -->
<Exec WorkingDirectory="$(OutputPath)" Command="del compilation*.config" />
<Exec WorkingDirectory="$(OutputPath)" Command="del connectionstrings*.config" />

</Target>

If I put this anywhere other than 'AfterBuild' it didn't seem to do anything.  I certainly learnt a bit more about aspnet_regiis, as I'd only used it previously to 'install' ASP.NET.  I also had to specify the path to aspnet_regiis, but you could obviously use a property for this (I'm new to MSBuild - only dipping in when I have to, so the framework path may already be a standard property?).

Replacement of system.web sections

The key thing here (which I don't believe was documented very well anywhere) is how to replace system.web elements.  Other typical replacements - e.g. appSettings or connectionStrings are children of the root config element.  You'd therefore assume that you need to replace the whole of system.web (which is a little inconvenient - but still worth it).  This doesn't work and you'll get a 'nice' WDP00002 error saying it can't find the system.web element (a bit like saying 'can't find printer' when it's right next to the computer!). 

You just have to go one level down as follows (in the Deployment --> Web.config file section replacements property page):

system.web/compilation=compilation.release.config

compilation.release.config may be as simple as...

<compilation debug="false"></compilation>

You might have a warning saying 'compilation' isn't a valid element, but this is just the intellisense barking as it validates against the config schema.

Encrypting Forms Authentication Passwords

This is pretty simple and there's lots of docs to support this, but I wanted a simple tool to generate the hash for a given string, and a quick google yielded a nice little command-line tool...

This way you can plug it into your build if you need to, but also replace for different environments using the techniques above.


posted on Thursday, May 22, 2008 9:57:27 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Wednesday, April 16, 2008

Tree Surgeon - Automatically creating a project source tree structure

Found Tree Surgeon on CodePlex.  Its implementation's actually a bit specific for my use, but has some nice ideas (based on Mike Roberts' articles on setting up a .NET Development tree

I'll keep it to hand I think...

posted on Wednesday, April 16, 2008 8:18:36 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Wednesday, February 06, 2008

Programmatically add Meta Tags to ASP.NET Master Pages using a SiteMap

I hadn't really used SiteMaps before, but it's a useful feature of ASP.NET 2.0 (most people using them to drive BreadCrumb controls and site navigation).

After realising there was no support for design of the sitemap (apart from the default text editor) I found a simple (and functional) SiteMap editor here.

I then realised that I needed to add meta tags for each page in the site (NOTE: this is a simple 'content' site, so don't come running to me if you have the need to generate specific tags based on what colour trousers the user is wearing!).

You can add new attributes very easily to the sitemap, and access them programmatically.  The editor has a nice little grid to make this extra easy for you.  In my example here we've added a 'keywords' attribute. 

Assuming you're using a treeview (or something similar) for navigation on your site and you're binding to the SiteMap data source, then you'll get the context of the current site map node in your page (or master page).

You can then use it to set your meta tags as follows:

You can also set Page.Title from currNode.Title etc.

This is actually quite a neat way to drive things like other standard properties of your master page - e.g. subheadings. 

posted on Wednesday, February 06, 2008 4:46:54 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Tuesday, February 05, 2008

Free Visual Studio 2005 Addins

I'm currently without ReSharper (eek), so am trying to make the best of it.  Looking around for VS 2005 addins (that I haven't already got), I stumbled across a few resources....

Carl J has a list of Free Addins, and, wait - there's another list...

Not forgetting Hanselman's (old) list

I'm sure there's more, but I don't want to overcapitalise and slow my machine down more than Resharper did (maybe I should look at CodeRush?)

posted on Tuesday, February 05, 2008 8:26:32 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Tuesday, January 08, 2008

Google Gadget for Connex Melbourne Service Status

I was playing around today and knocked up a quick Google Gadget (for use with iGoogle) for people in Melbourne to monitor the status of the train services delivered by Connex.  This is available on the Connex site, but conveniently sits in its own frame and so was easy to port to a 'gadget'.

No real rocket science (and the gadget editor's a bit buggy) - but I love the concept...

Click Add to Google to add it to your iGoogle Page

I've posted it to the 'directory' so it will hopefully be accessible from the main 'gadget search' soon...

posted on Tuesday, January 08, 2008 2:51:53 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Thursday, September 06, 2007

PSExpect - PowerShell Unit Testing Library

What next - COBOLUnit?  Haven't tried this yet, but this could be a good tool for 'self-diagnostic' tests on Servers...

http://www.codeplex.com/psexpect/Wiki/View.aspx?title=User%20Guide&referringTitle=Home

posted on Thursday, September 06, 2007 7:53:06 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Wednesday, August 08, 2007

Windows Powershell - Batch files with knobs on

Just downloading a few .NET 3.0 bits and pieces from MS and happened across some 'you might like to download this' links.  Once of which was Windows PowerShell.  Haven't tried it yet, but will certainly give it a go in my next 'scripting' escapade.

Microsoft have obviously developed this quite a lot based on the amount of documentation I've not had chance to look at yet!

posted on Wednesday, August 08, 2007 10:37:05 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]

ReSharper - Getting more out of it

ReSharper's got plenty of functionality (more of which I find and use as time goes on), but I found this on Joe White's blog that is certainly a useful reference and taught me a couple of new things too..

posted on Wednesday, August 08, 2007 8:02:25 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]