RSS 2.0 | Atom 1.0 | CDF

Search

Categories

Archive

Blogroll

Sign In

# Sunday, January 01, 2012
Sunday, January 01, 2012 10:19:39 PM (GMT Standard Time, UTC+00:00) ( .Net General | Database )

Data truncated to 255 characters with Excel Jet/ODBC driver: http://support.microsoft.com/kb/189897

Solution: http://support.sas.com/kb/31/765.html
My Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Jet\4.0\Engines\Excel

Set to 0 to avoid the truncation problem!

Comments [0] | | # 
# Wednesday, December 28, 2011
Wednesday, December 28, 2011 9:16:30 PM (GMT Standard Time, UTC+00:00) ( .Net General | Asp.Net )
I'm using Asp.Net to open excel files and read the values cell by cell, normally i prefer to import Excel files as a datatable, but some documents don't fit the strict rows/columns format required for importing into a DataTable.  the code i'm using is based on this from dotnetperls.  worked fine for the most part on my dev PC but I had a few issues trying to get this to work on a 32bit version of excel on a 64 bit Windows 2008 server.
here's what i had to do to get it working:
  • disable macros (throwing exceptions opening the workbook), requires a reference to the Microsoft.Office.Core assembly:
    excelApp.AutomationSecurity = Microsoft.Office.Core.MsoAutomationSecurity.msoAutomationSecurityForceDisable;   // disable macros
  • give launch / open permissions to the Excel application in DCOM config, this was tricky because it isn't shown in a 64 bit OS if the program is 32 bit, i found the answer here.
  • set the identity in DCOM for Excel to the interactive user.


Comments [0] | | # 
# Tuesday, September 14, 2010
Tuesday, September 14, 2010 12:50:05 PM (GMT Daylight Time, UTC+01:00) ( .Net General | Database )
if you use SqlMetal to generate DataContext classes against an SQL database, you might run into some problems with GridView and deleting a row, when using a LinqDataSource.
this was a very frustrating problem to track down, apparently there is a bug in the LinqDataSource with datetime fields.  i kept getting this error: ChangeConflictException: Row not found or changed
and there was no apparent reason why it was happening, because the same code worked for other tables.  I eventually narrowed it down to the only difference between the two tables, a non nullable datetime field. changing this field to nullable removed the problem.
this thread was useful in troubleshooting the problem.
i also found that calling DataBind() on the LinqDataSource before re-binding the GridView helped get rid of this error message.

Comments [0] | | # 
# Tuesday, April 21, 2009
Tuesday, April 21, 2009 1:30:58 PM (GMT Daylight Time, UTC+01:00) ( .Net General | Asp.Net )
I got a request from a client for a 'mini' navigation menu to appear on a page to allow the user to 'jump' to the various sections of the page.  Maintaining a set of named anchor hyperlinks in a document (with a javascript-based select menu) is not really an option for someone who doesn't know HTML, which is most people who use content management.  In the wrong hands this creates more trouble than it's worth. 
so i started thinking. the documents in question are well structured, using paragraphs and headings, as enforced by the content management system.  i worked out this solution which is exactly what i need, a maintenance free javascript jump menu, that doesn't add clutter to the document with named anchors.  it assumes that the all H2 tags should be displayed as links in the menu.  the javascript iterates through the H2 tags and loads up the menu, with a ScrollTo function used when an item is selected.

here's a screenshot


The code for the HTML page:
<script src="/JumpMenu.js" type="text/javascript"></script>
<select id="jumpNavSelect" name="jumpNavSelect" onchange="JumpToHeading(this.selectedIndex)">
<option value="">On this page...</option>
</select>
and the JumpMenu.js file
var array;

function CreateAnchorMenu() {
array = document.getElementsByTagName("h2");
var src = document.getElementById('jumpNavSelect');
// iterate through all H2 headings, add dropdown items for each.
for (var i = 0; i < array.length; i++) {
var heading = array[i];
var text = heading.firstChild.nodeValue;
if (!text)
continue;
src[i+1] = new Option(text, i);
}
}

function JumpToHeading(HeadingIndex) {
if (HeadingIndex == 0)
return;
var heading = array[HeadingIndex-1];
ScrollToElement(heading);
}

function ScrollToElement(theElement) {
var selectedPosX = 0;
var selectedPosY = 0;
while (theElement != null) {
selectedPosX += theElement.offsetLeft;
selectedPosY += theElement.offsetTop;
theElement = theElement.offsetParent;
}
window.scrollTo(selectedPosX, selectedPosY);
}

window.onload = CreateAnchorMenu;
Comments [3] | | # 
# Friday, February 27, 2009
Friday, February 27, 2009 5:47:52 PM (GMT Standard Time, UTC+00:00) ( .Net General | Asp.Net )
Cstr(CDate({Table.Column}), "dd/MM/yyyy")

Comments [0] | | # 
# Thursday, October 16, 2008
Thursday, October 16, 2008 7:05:36 PM (GMT Daylight Time, UTC+01:00) ( .Net General )
thanks to Kev for his brilliant post about solving this problem, where you know you have the correct XPath expression but .Net is not giving you any nodes :(
his solution is the correct one, an alternative approach is to remove the xmlns from the root element before you load the XmlDocument so that you can use the default empty namespace (or something like that).

Comments [0] | | # 
# Wednesday, August 20, 2008
Wednesday, August 20, 2008 3:00:46 PM (GMT Daylight Time, UTC+01:00) ( .Net General )
i assumed that File.WriteAllText() would assume UTF8 encoding since all strings are unicode in .net by default, so i never bothered specifying the encoding in the 3rd parameter.  But it was causing me grief with accented characters, and i finally worked it out that i needed to specify UTF8 explicitly.
File.WriteAllText(filepath, text, Encoding.UTF8);

Comments [0] | | # 
# Thursday, July 17, 2008
Thursday, July 17, 2008 5:25:32 PM (GMT Daylight Time, UTC+01:00) ( .Net General | Asp.Net )
if you ever send a string across a web service, and write it out to a file, make sure you specify Encoding.UTF8 explicitly. otherwise characters such as the EURO symbol may not render correctly in some browsers (IE6).  it took me ages to pin this down, because everything i read about was about HTTP header charset values, or HTML document charsets, or database encodings.  In my case i was using the default encoding and this messed up EURO symbols. I suspect it is because of the string being serialized in the web service, but haven't the time to look into it any further.  it's fixed now anyhow.

Comments [3] | | # 
# Thursday, January 31, 2008
Thursday, January 31, 2008 4:32:13 PM (GMT Standard Time, UTC+00:00) ( .Net General | Asp.Net )
this wasn't very obvious to work out, and i couldn't find a decent solution online, so i came up with this.
this.DropDownList1.Attributes.Add("onchange", "if(!confirm('Are you sure you want to delete this item?')) return;");
This works with .Net 2 and 3 and 3.5, but may break with future versions if .Net changes the javascript code for AutoPostBack drop down lists.  The reason it works now is because .Net adds the custom 'onchange' attribute before it's on doPostBack() function call.  So if we return from the confirm prompt, the form will not submit. 

Comments [0] | | # 
# Tuesday, November 27, 2007
Tuesday, November 27, 2007 11:32:12 AM (GMT Standard Time, UTC+00:00) ( .Net General | Asp.Net | Windows Server 2003 )
if, like me, you were running VS 2008 Beta 2 on a server and you had crystal reports working fine, and then recompiled your projects in VS 2008 RTM, you may get an error like this when you try and create a crystal report on a production server:
System.Runtime.InteropServices.COMException (0x800736B1):
Retrieving the COM class factory for component with CLSID
{5FF57840-5172-4482-9CA3-541C7878AE0F} failed due to the following error:
800736b1. at CrystalDecisions.CrystalReports.Engine.ReportDocument..cctor()
In my case, i had installed Crystal Reports Runtime for VS 2008 Beta 2, and i never installed the runtime for the RTM version.  it should be available on your VS machine, if you opted for CR in the VS installer:
C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\CrystalReports10_5

should probably uninstall the old version of the CR runtime while you're at it too. 

Comments [2] | | # 
# Monday, November 19, 2007
Monday, November 19, 2007 11:20:35 AM (GMT Standard Time, UTC+00:00) ( .Net General )
how weird is this.  i upgraded my CMS editor to XHTML and i suddenly find that any pages with an IFRAME have the footer cut off, completely invisible.  i found out it was because the self-closing tag (as per XHTML) screws up the page layout in IE and FF.  you have to use </iframe>, which unfortunately breaks XHTML validation, but i can live with that.
for more info see this blog post that explains it further.

Comments [0] | | # 
# Friday, November 16, 2007
Friday, November 16, 2007 12:16:50 PM (GMT Standard Time, UTC+00:00) ( .Net General | .Net Windows Forms | Asp.Net )

Note: this doesn't really work..

i'm just leaving it here for interest.  the approach will work for the first invocation of either v2 or v3 client, but after a v2 client has used the web service, any v3 clients will fail.  MS told me this is not supported because of the 2 soapExtensions interfere with each other in the pipeline etc


My asp.net CMS has some winforms clients running WSE2, and i'm doing an upgrade to the client (and server) to use WSE3, but i want to support both versions.  Both WSE2 and WSE3 clients are using a CustomUsernameTokenManager.  it was a bit tricky to work out, but here is what i ended up doing. thanks to brian o'keefe for his newsgroup post for most of the answer.  Your exact config might vary but hopefully it will save you some of the hassle of working all this out.
  • Leave the existing web service in place, e.g. Service_WSE2.asmx
  • Create a new web service for the WSE3 client, essentially copy/paste the ASMX file, e.g. Service_WSE3.asmx
  • Create a new CustomUsernameTokenManager for the WSE3 service which inherits from Microsoft.Web.Services3.Security.Tokens.UsernameTokenManager.  In my case it was as easy as copy/paste from the WSE2 token manager and change all the WSE2 namespaces to WSE3.
  • then in web.config, make sure both WSE config sections are listed:
    <configuration>
    <configSections>
    <section name="microsoft.web.services3" type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration, Microsoft.Web.Services3,Version=3.0.0.0,Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <section name="microsoft.web.services2" type="Microsoft.Web.Services2.Configuration.WebServicesConfiguration, Microsoft.Web.Services2, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </configSections>
  • add in references to both the assemblies for the compilation section (not 100% sure if this is necessary, but at least it means the app will not compile on the production server if you forget to deploy either of the WSE assemblies, instead of waiting till one of your clients connects).

    	<compilation defaultLanguage="c#" debug="true">
    <assemblies>
    <add assembly="Microsoft.Web.Services2, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add assembly="Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  • remove the <webServices> section from <system.web>, because each web service should be configured by location, not global as this interferes.

    	<location path="Service_WSE2.asmx">
    <system.web>
    <webServices>
    <soapExtensionTypes>
    <add type="Microsoft.Web.Services2.WebServicesExtension, Microsoft.Web.Services2, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" priority="1" group="0" />
    </soapExtensionTypes>
    </webServices>
    </system.web>
    </location>

    <location path="Service_WSE3.asmx">
    <system.web>
    <webServices>
    <soapExtensionTypes>
    <clear/>
    </soapExtensionTypes>
    <soapServerProtocolFactory type="Microsoft.Web.Services3.WseProtocolFactory, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    <soapExtensionImporterTypes>
    <add type="Microsoft.Web.Services3.Description.WseExtensionImporter, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    </soapExtensionImporterTypes>
    </webServices>
    </system.web>
    </location>
  • my webservice policy are defined in policyCache.config files, so the following web.config sections point each version of WSE to the right file:
    	<microsoft.web.services3>
    <policy fileName="policyCache_WSE3.config"/>
    <security>
    <securityTokenManager>
    <add type="Whatever.CustomUsernameTokenManager3" namespace="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" localName="UsernameToken"/>
    </securityTokenManager>
    </security>
    </microsoft.web.services3>
    <microsoft.web.services2>
    <messaging>
    <maxRequestLength>10000</maxRequestLength>
    </messaging>
    <security>
    <securityTokenManager type="Whatever.CustomUsernameTokenManager2, MyAssemblyName" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    qname="wsse:UsernameToken" />
    <defaultTtlInSeconds>86400</defaultTtlInSeconds>
    <timeToleranceInSeconds>86400</timeToleranceInSeconds>
    </security>
    <policy>
    <cache name="policyCache_WSE2.config" />
    </policy>
    </microsoft.web.services2>
  • and lastly, the policy cache files themselves:
    WSE 3 version
    <?xml version="1.0"?>
    <policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
    <extensions>
    <extension name="usernameOverTransportSecurity" type="Microsoft.Web.Services3.Design.UsernameOverTransportAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <extension name="requireActionHeader" type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </extensions>
    <policy name="usernameTokenSecurity">
    <usernameOverTransportSecurity />
    <requireActionHeader />
    </policy>
    </policies>
  • WSE 2 version
    <?xml version="1.0"?>
    <policyDocument xmlns:wse="http://schemas.microsoft.com/wse/2003/06/Policy" xmlns="http://schemas.microsoft.com/wse/2003/06/Policy">
    <mappings xmlns:wse="http://schemas.microsoft.com/wse/2003/06/Policy">

    <endpoint uri="http://localhost/Service_WSE2.asmx">
    <defaultOperation>
    <request policy="#username-token-signed" />
    <response policy="" />
    <fault policy="" />
    </defaultOperation>
    </endpoint>

    <defaultEndpoint>
    <defaultOperation>
    <request policy="" />
    <response policy="" />
    <fault policy="" />
    </defaultOperation>
    </defaultEndpoint>

    </mappings>
    <policies xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <wsp:Policy wsu:Id="username-token-signed" xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wssp="http://schemas.xmlsoap.org/ws/2002/12/secext">
    <wsp:MessagePredicate wsp:Usage="wsp:Required" Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
    wsp:Body() wsp:Header(wsa:To) wsp:Header(wsa:Action) wsp:Header(wsa:MessageID) wse:Timestamp()
    </wsp:MessagePredicate>
    <wssp:Integrity wsp:Usage="wsp:Required">
    <wssp:TokenInfo>
    <SecurityToken xmlns="http://schemas.xmlsoap.org/ws/2002/12/secext">
    <wssp:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken</wssp:TokenType>
    <wssp:Claims>
    <wssp:UsePassword wsp:Usage="wsp:Required" />
    </wssp:Claims>
    </SecurityToken>
    </wssp:TokenInfo>
    <wssp:MessageParts Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
    wsp:Body() wsp:Header(wsa:Action) wsp:Header(wsa:FaultTo) wsp:Header(wsa:From) wsp:Header(wsa:MessageID) wsp:Header(wsa:RelatesTo) wsp:Header(wsa:ReplyTo) wsp:Header(wsa:To) wse:Timestamp()
    </wssp:MessageParts>
    </wssp:Integrity>
    </wsp:Policy>
    </policies>
    </policyDocument>

Comments [1] | | # 
# Tuesday, September 18, 2007
Tuesday, September 18, 2007 1:43:58 PM (GMT Daylight Time, UTC+01:00) ( .Net General | Database )
wow, i never knew this existed: http://sqlprofiler.googlepages.com/
thanks to nikolay.zhebrun
it works great.

Comments [0] | | # 
Tuesday, September 18, 2007 1:22:32 PM (GMT Daylight Time, UTC+01:00) ( .Net General | Asp.Net | Database )
Here is the sql that is generated by a DataContext when you use Skip() and Take() to efficiently select records for the grid:
SELECT TOP 10 [t1].[Name], [t1].[Address], [t1].[Tel1], [t1].[Tel2], [t1].[Email], [t1].[DateCommenced], [t1].[Comments], [t1].[Active], [t1].[Fax]
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t0].[Name], [t0].[Address], [t0].[Tel1], [t0].[Tel2], [t0].[Email], [t0].[DateCommenced], [t0].[Comments], [t0].[Active], [t0].[Fax]) AS [ROW_NUMBER], [t0].[Name], [t0].[Address], [t0].[Tel1], [t0].[Tel2], [t0].[Email], [t0].[DateCommenced], [t0].[Comments], [t0].[Active], [t0].[Fax]
FROM [dbo].[Table1] AS [t0]
) AS [t1]
WHERE [t1].[ROW_NUMBER] > @p0
-- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [50]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

it's not immediately obvious why they use the subquery like this, but i'm sure they have been very thorough in optimising LINQ. 

Comments [1] | | # 
# Monday, September 17, 2007
Monday, September 17, 2007 3:01:26 PM (GMT Daylight Time, UTC+01:00) ( .Net General )
Today was the first time i used LINQ with non-database data.  I always knew you could do it but didn't realise how satisfying it would actually be to use LINQ to crunch out some results that i would otherwise have to code up in some gnarly routine involving on-the-fly DataTables and DataView filters etc.  Also, I'm mainly posting this because i can never remember the LINQ group by syntax...

i have a plain list of reference numbers, e.g. 1.a, 2.b, 3.c, 4.a, 10.d, 11.c etc.  they contain duplicates and i was asked to figure out the top 10 most popular references.  A simple regex separates out the numbers, i could also have used string.split() but regex is better in my case.  Copy each one into an array and then a simple LINQ query on the array gives me the top 10 references:
List<string> lst = new List<string>();
foreach(Match m in Regex.Matches(this.txtInput.Text, @"\w+\.\w+", RegexOptions.IgnoreCase))
lst.Add(m.Groups[0].Captures[0].Value);
var results = (from l in lst group l by l into g select new {Ref = g.Key, Total = g.Count()}).OrderByDescending(z => z.Total).Take(10);
foreach(var v in results)
this.txtOutput.Text += v.Ref + "\t" + v.Total + "\r\n";

Comments [0] | | # 
# Friday, August 31, 2007
Friday, August 31, 2007 12:36:35 PM (GMT Daylight Time, UTC+01:00) ( .Net General | Database )
It's fair enough that SQL won't accept a parameterised query like below, because it cannot verify that the parameter is referring to a valid column.
select * from table order by @OrderBy
the work around then is to use a case statement like so:
select * from table order by case 
when @OrderBy = 'Column1' then Column1
when @OrderBy = 'Column2' then Column2
end
but i ran into a problem with this approach, where sql raises an error if the datatypes of the columns are not all the same, e.g. you may want to sort by an Int or NVarChar column.  the datatype precedence rules applied to the case statement are well explained in this post on google groups.  the solution posted by Erland Sommarskog is to have a separate case statement for each clause.  so the more robust approach is like so:
select * from table order by 
case when @OrderBy = 'Column1' then Column1 end,
case when @OrderBy = 'Column2' then Column2 end



Comments [0] | | # 
# Friday, July 27, 2007
Friday, July 27, 2007 5:49:16 PM (GMT Daylight Time, UTC+01:00) ( .Net General | .Net Windows Forms | Asp.Net )
Just got up and running with VS 2008 Beta 2 and converted my Orcas Beta 1 projects over to VS 2008.  have a read of Scott Gu's post on downloading and installing.  whaddya know? VS 2008 hasn't crashed yet :)  seriously though, this is a major relief to see good stability.  as a developer it did my head in to have to keep nuking devenv.exe and losing a few minutes work, driving me quietly insane.  hopefully those days are over now.  Beta 1 was a thousand times better than VS 2005, but Beta 2 looks even better. 

in terms of breaking changes with upgrading projects etc., here are some tidbits i came across
  • the namespace System.Data.Linq.Expressions no longer exists.  i just deleted this namespace and it worked fine, they must have moved various classes back into the root Linq namespace or something.
  • if you use SqlMetal, you will have to use the new version of this tool, which i found at C:\Program Files\Microsoft SDKs\Windows\V6.0A\Bin\SqlMetal.exe. The generated code from the new tool is very different to before so you will need to regenerate in order to use the new LINQ libraries.
  • web.config changes are also introduced.  the best way to figure out the changes is to open up a blank web application and compare the standard web.config in the empty project to your own web.config.
  • Crystal Reports assembly version numbers are the same with this release of VS, 10.5.3700.0, so no changes here since Beta1.
i'll post more next week when i have some real experience using VS 2008.  so far so good.  great job Scott & Co.

Update 30 July 2007 - Deployment Issues
  • To deploy a VS 2008 beta 2 web application to a Server 2003 with .net runtime 2.0, you need to install 3.5 of the runtime.  i got all sorts of web.config errors due to the new syntax with beta 2.  A web app compiled against v3.5 in Beta 1 had no trouble running on top of the 2.0 runtime with the various v3.5 DLLs deployed to the /bin directory, however this doesn't seem to be possible anymore.  it took me a while to find the correct download for the 3.5 beta 2 redistributable, it installed in about 10 mins and required a reboot, and did not affect any of the v1.1 and v2 web sites running on the server. 

Comments [0] | | # 
# Wednesday, July 18, 2007
Wednesday, July 18, 2007 5:41:41 PM (GMT Daylight Time, UTC+01:00) ( .Net General | .Net Windows Forms )
public static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for(j = 0; j < encoders.Length; ++j)
if(encoders[j].MimeType == mimeType)
return encoders[j];
return null;
}

public static Bitmap resizeImage(Bitmap originalImage, int max)
{
// never increase the size of an image from this method because it loses resolution
if(originalImage.Height <= max && originalImage.Width <= max)
return originalImage;

int height, width;
if(originalImage.Width > originalImage.Height)
{
width = max;
height = (int)(max/((double)originalImage.Width / (double)originalImage.Height));
}
else
{
height = max;
width = (int)(max/((double)originalImage.Height / (double)originalImage.Width));
}

Bitmap thumb = new Bitmap(originalImage as Image, width, height);
Graphics resizer = Graphics.FromImage(thumb);
resizer.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
resizer.DrawImage(originalImage, 0, 0, width, height);

resizer.Dispose();
return thumb;
}

public static bool ThumbnailCallback()
{
return false;
}

this lovely code highlighted in red solved problems i was having with resizing JPEGs using .net.   without the code in red, some images would appear very grainy when reduced in size.  i got this tip from http://forums.asp.net/p/532033/532033.aspx.


Comments [0] | | # 
# Tuesday, June 26, 2007
Tuesday, June 26, 2007 4:48:13 PM (GMT Daylight Time, UTC+01:00) ( .Net General | General )
Just in case anyone was as stuck as i was today trying to virtually load an ISO image.  the Virtual CD Control Panel thing doesn't work in Vista, and for some reason VirtualCloneDrive wouldn't work for me either.
i eventually found a free tool called PowerISO which works great.  you can create several virtual drives and mount an ISO in each one.  i'm currently installing Orcas Beta 1 from an ISO mounted across the network, i wasn't sure if it could do that but it had no problems.

Comments [0] | | # 
# Thursday, May 17, 2007
Thursday, May 17, 2007 5:36:06 PM (GMT Daylight Time, UTC+01:00) ( .Net General | Asp.Net )

I know we're not supposed to be going live with .Net 3.5 / Orcas Beta 1 yet, but hey. 

I ran into a Crystal Reports deployment problem with an asp.net web application developed in Orcas beta 1, using the bundled version of crystal reports.  The problem is that the crystal report dlls used in a Beta 1 project are 10.5.3700.0 but there don't appear to be any merge modules available to support this version number, so there is no supported way to run the Orcas version of Crystal Reports on a server, without installing Orcas itself.  I tried numerous options of digging out the 10.5.3700.0 dlls from program files\common files\business objects etc and putting them in the web site bin directory on the server, but that didn't work.  The obvious error message that comes up is as follows:

Could not load file or assembly 'CrystalDecisions.CrystalReports.Engine, 
Version=10.5.3700.0, Culture=neutral, PublicKeyToken=692fbea5521e1304' or one of its dependencies.
The system cannot find the file specified.

what i ended up doing was instructing the web application to bind to the 10.2.3600.0 versions of the assemblies, which are already deployed using the normal CR server install.

here is what i added to the end of my web.config file to get it going:

     ....
<runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="CrystalDecisions.CrystalReports.Engine" publicKeyToken="692fbea5521e1304"/>
                <bindingRedirect oldVersion="10.5.3700.0" newVersion="10.2.3600.0"/>
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name="CrystalDecisions.CrystalReports.Shared" publicKeyToken="692fbea5521e1304"/>
                <bindingRedirect oldVersion="10.5.3700.0" newVersion="10.2.3600.0"/>
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name="CrystalDecisions.Shared" publicKeyToken="692fbea5521e1304"/>
                <bindingRedirect oldVersion="10.5.3700.0" newVersion="10.2.3600.0"/>
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
</configuration>
Comments [1] | | # 
# Wednesday, May 02, 2007
Wednesday, May 02, 2007 6:43:46 PM (GMT Daylight Time, UTC+01:00) ( .Net General | Asp.Net )
i spent hours trying to troubleshoot this.  obviously the error makes no sense because CR should not be logging on to anything in a dataset scenario.
anyway, a thousand thanks to Jason for his post on the MSDN forums with the simple answer: set the datasource to the DataTable, not the DataSet.

i can't wait to lose Crystal reports altogether and move to the microsoft reporting thingy, i haven't had time to play about with it yet but at least it will be properly programmed and it won't contain the 10 years of bugs that crystal reports have carried through each release of their software.  </RANT>

Comments [0] | | # 
# Monday, April 30, 2007
Monday, April 30, 2007 11:50:28 AM (GMT Daylight Time, UTC+01:00) ( .Net General | Asp.Net )
here are a few of my main findings/hurdles encountered when upgrading May CTP web projects to Orcas beta 1 Web Application Projects.  I'm targeting version 3.5 of the framework for deployment on a Server 2003 with .Net 2.0 runtime installed, i will update this article if it doesn't work out. 
  • if you had any code in the "App_Code" folder, VS will probably have set the compile action to "content", it should be changed to "compile".  otherwise you will get errors like: The type or namespace name 'xyz' could not be found (are you missing a using directive or an assembly reference?)
  • you'll have to remove any reference to the System.Query and System.Expressions, these are not part of the new LINQ spec.
  • Replace System.Data.DLinq with System.Data.Linq.
  • if you want to use any LINQ expressions, like "from x in db.Table select x" then you need to include the System.Linq namespace.  otherwise you will get errors like 'System.Data.Linq.Table<xyz>' does not contain a definition for 'Where' and no extension method 'Where' accepting a first argument of type 'System.Data.Linq.Table<xyz>' could be found (are you missing a using directive or an assembly reference?)
  • you will also need to include System.Linq.Expressions if you are using "language-level code expressions to be represented as objects in the form of expression trees".
The old LINQ assemblies are versioned 1.0.2319.19044 (May CTP) but the new ones for Beta 1 are versioned 2.0.0.0.  Have a check through the referenced assemblies in your project to make sure you have the latest versions. 

Crystal reports

look out for the new crystal report assemblies, 10.5.3700.0.  a web app referencing the new versions in web.config will complain if these versions are not available on the server.  i'll update soon when i have found out how to do this.

Comments [0] | | # 
# Friday, March 09, 2007
Friday, March 09, 2007 5:43:24 PM (GMT Standard Time, UTC+00:00) ( .Net General | Asp.Net | Database | General )
If you run a web server, chances are you have some form of automated monitoring system in place.  If you use MOM or another enterprise level thing then this post won't be of much relevance.  If, like me, you have simpler requirements, read on.

I have been caught out a few times with my web sites being down because Windows Server 2003 automatically installed an update and something went wrong and IIS got stopped, or like this morning at 4am, SQL 2005 SP2 failed to install and left the SQL Service offline.  i didn't find out till i got a phone call from a client.

My datacenter provide very good ping monitoring with SMS alerts etc., but this is not a complete solution because the web site may have a configuration error, and it will still respond to pings.  similarly, you can't just check for an OK HTTP status code because your error ASPX page may not be configured to send an error HTTP status code.

I have used various online web site monitoring services, with varying degrees of success / satisfaction.  My current provider are InternetVista.com and for €70 a year i get a 10 minute check for a single HTTP site, with a keyword match on the contents of the page, and an email/sms alert if the match is not found.  You can pay for extra and more frequent checks, but €70 is as much as i think the service is worth.  To have this level of checking done on 10 sites would cost a lot, so to save a few quid i wrote a very simple aspx page that does a series of tests on all the resources i want to verify on the server, e.g. SQL Server, MS Access, IIS web sites.  The aspx code is listed below, i wrote it inline rather than compiled/dll because it is easier to deploy in an existing web site without any risk of any side effects (dll collisions), it should be straight forward to understand for a c# programmer.  let me know if you have questions.  It runs in a few miliseconds on my server so i'm not worried about polling all these resources every 10 mins.



Run_Server_Tests.aspx code:
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Collections" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.OleDb" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Net" %>

<script RunAt="server">

/* Server Monitoring Script:
* - test SQL databases by running an sql string against an SQL connection string
* - test Access databases by running an sql string against a JET connection string
* - test web sites by Regex matching a search string against the contents of a HttpWebRequest
*/

enum TestType {Sql_Server, Ms_Access, Http_Request } // different types of supported requests

/// <summary>
/// Container class to represent a 'test' object for a resource on the server.
/// </summary>
class TestObject
{
public TestType Type; // e.g. Sql_Server.
public string TestString; // e.g. connection string for a database. or URI for http request.
public string TestParam; // e.g. sql string for a database. or search string for a http request.

public TestObject(TestType type, string testString, string testParam)
{
this.Type = type;
this.TestString = testString;
this.TestParam = testParam;
}
}

void Page_Load(object sender, EventArgs e)
{
List<TestObject> tests = new List<TestObject>();

tests.Add(new TestObject(TestType.Sql_Server, @"Data Source=.\SQLEXPRESS;Initial Catalog=DB1;Integrated Security=True", "select top 10 * from Table1"));
tests.Add(new TestObject(TestType.Sql_Server, @"Data Source=.\SQLEXPRESS;Initial Catalog=DB2;Integrated Security=True", "select top 10 * from Table1"));
tests.Add(new TestObject(TestType.Sql_Server, @"Data Source=.\SQLEXPRESS;Initial Catalog=DB3;Integrated Security=True", "select top 10 * from Table1"));
tests.Add(new TestObject(TestType.Sql_Server, @"Data Source=.\SQLEXPRESS;Initial Catalog=DB4;Integrated Security=True", "select top 10 * from Table1"));

tests.Add(new TestObject(TestType.Ms_Access, @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Inetpub\Database\DB5.mdb;Persist Security Info=True", "select top 10 * from Table1"));
tests.Add(new TestObject(TestType.Ms_Access, @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Inetpub\Database\DB6.mdb;Persist Security Info=True", "select top 10 * from Table1"));

tests.Add(new TestObject(TestType.Http_Request, "http://mysite1.ie/", "Site 1"));
tests.Add(new TestObject(TestType.Http_Request, "http://mysite2.ie/", "Site 2"));
tests.Add(new TestObject(TestType.Http_Request, "https://mysite3.ie/", "Site 3"));
tests.Add(new TestObject(TestType.Http_Request, "https://mysite4.ie/", "Site 4"));
tests.Add(new TestObject(TestType.Http_Request, "https://mysite5.ie/", "Site 5"));

int numCompleted = 0;
int numFailed = 0;

// write the HTML header. (a result is flushed to the client after each test finishes.)
Flush(@"
<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>
<html>
<head>
<title>Server Test</title>
<meta name='ROBOTS' content='NOINDEX,NOFOLLOW'>
<link rel='stylesheet' type='text/css' href='ServerTestStyles.css' />
</head>
<body>");

foreach(TestObject test in tests)
{
try
{
switch(test.Type)
{
case TestType.Sql_Server:
runQuerySql(test.TestParam, test.TestString);
break;
case TestType.Ms_Access:
runQueryOleDb(test.TestParam, test.TestString);
break;
case TestType.Http_Request:
string pageContents = new WebClient().DownloadString(test.TestString);
if(!Regex.IsMatch(pageContents, test.TestParam, RegexOptions.IgnoreCase))
throw new Exception("Search string not found: " + test.TestParam);
break;
default:
throw new Exception("Test type not handled " + test.Type);
}
Flush(String.Format("<span class='pass'>Pass</span> &nbsp; <span class='type'>{0}</span> &nbsp; {1} <hr />", test.Type, test.TestString));
numCompleted++;
}
catch(Exception ex)
{
Flush(String.Format("<span class='fail'>Fail</span> &nbsp; {1} <span class='type'>{0}</span><BR><span class='error'>{2}</span><hr />", test.Type, test.TestString, ex.Message));
numFailed++;
}
}
if(numFailed > 0)
Flush(String.Format("<h1>{0} errors occured</h1>", numFailed));
else
Flush(String.Format("<h1>All Good!</h1>", numFailed)); // if you use this page with an automated monitoring service, look for "All Good" in the page contents. otherwise an error occured
Flush("</body></html>");
}

/// <summary>
/// Method to run an sql string against an sql database
/// </summary>
public static DataSet runQuerySql(string sql, string connString)
{
SqlConnection conn = new SqlConnection(connString);
DataSet ds = new DataSet();
SqlDataAdapter dba = new SqlDataAdapter();
SqlCommand cmd = new SqlCommand(sql, conn);

try
{
dba.SelectCommand = cmd;
dba.Fill(ds, "Table");
return (ds);
}
catch(Exception e)
{
throw e;
}
finally
{
cmd.Connection.Close();
conn.Close();
}
}

/// <summary>
/// Method to run an sql string against an Access database
/// </summary>
public static DataSet runQueryOleDb(string sql, string connString)
{
OleDbConnection conn = new OleDbConnection(connString);
DataSet ds = new DataSet();
OleDbDataAdapter dba = new OleDbDataAdapter();
OleDbCommand cmd = new OleDbCommand(sql, conn);

try
{
dba.SelectCommand = cmd;
dba.Fill(ds, "Table");
return (ds);
}
catch(Exception e)
{
throw e;
}
finally
{
cmd.Connection.Close();
conn.Close();
}
}

/// <summary>
/// Flush output to the browser (useful to indicate which tests are causing any delay)
/// </summary>
/// <param name="output"></param>
private void Flush(string output)
{
Response.Write(output);
Response.Flush();
}

</script>

ServerTestStyles.css:  (just to make the output more legible)

body
{
font-size: 90%;
font-family: Calibri, Helvetica, Sans-Serif;
padding: .5em;
}

hr
{
color: #87ceeb;
background-color: #87ceeb;
margin: .3em 0 .3em 0;
padding: 0;
height: 1px;
}

.pass
{
color: Blue;
font-weight: bold;
}
.fail
{
color: Red;
font-weight: bold;
}
.type
{
color: purple;
font-weight: bold;
}
.error
{
color: Red;
font-size: small;
}

I have configured the test in InternetVista to search for "All Good" in the url for the test page.  If this isn't present, i'll get an SMS/email alert and i can go and see what exactly is wrong.  It should be fairly easy to add other test types if you have different resources you need to check on.
Enjoy.

Comments [3] | | # 
# Friday, March 02, 2007
Friday, March 02, 2007 9:58:51 AM (GMT Standard Time, UTC+00:00) ( .Net General | Asp.Net )
Here is the event log crash entry:

Faulting application devenv.exe, version 8.0.50727.762, time stamp 0x45716759, faulting module craxddrt.dll_unloaded, version 0.0.0.0,
time stamp 0x43068582, exception code 0xc0000005, fault offset 0x0d26e30f, process id 0x1180, application start time 0x01c75cae13960a41.

it would crash completely randomly, even if i had closed all crystal reports and was working in a style sheet or other such harmless file. As i later found out, it was because the solution opened by default with the last crystal report i was working on, and this seemed to start some crystal report ActiveX thing that craps out on Vista.  sometimes it would take 2 minutes and sometimes 5 minutes, but it would always crash, even if i closed the report straight away.  The only way to get rid of it was to load up VS, close the report immediately and safely close VS before it got a chance to crash!  then next time it opens, there is no crystal report and the ActiveX control never loads.  it works ok now, although this is just one of a long list of complaints i have with VS 2005.  i think i'm allergic to crystal reports.

Comments [5] | | # 
# Friday, February 23, 2007
Friday, February 23, 2007 5:51:46 PM (GMT Standard Time, UTC+00:00) ( .Net General | Asp.Net )
The full error i got was below:
Error    35    The type 'CrystalDecisions.Shared.ExportFormatType' exists in both 
'c:\Windows\assembly\GAC\CrystalDecisions.Shared\9.1.5000.0__692fbea5521e1304\CrystalDecisions.Shared.dll' and
'c:\Windows\assembly\GAC_MSIL\CrystalDecisions.Shared\10.2.3600.0__692fbea5521e1304\CrystalDecisions.Shared.dll'  
The reason is because version 9 and 10 of crystal reports are installed on my dev box and VS needed help deciding which one to use.  the fix was to specify the exact assembly binding to use in web.config, as follows:
	<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="CrystalDecisions.CrystalReports.Engine" publicKeyToken="692fbea5521e1304" />
<bindingRedirect oldVersion="9.1.5000.0" newVersion="10.2.3600.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="CrystalDecisions.CrystalReports.Shared" publicKeyToken="692fbea5521e1304" />
<bindingRedirect oldVersion="9.1.5000.0" newVersion="10.2.3600.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="CrystalDecisions.Shared" publicKeyToken="692fbea5521e1304" />
<bindingRedirect oldVersion="9.1.5000.0" newVersion="10.2.3600.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
thanks to rick ersek for the solution.

Comments [2] | | # 
# Sunday, February 18, 2007
Sunday, February 18, 2007 11:42:07 AM (GMT Standard Time, UTC+00:00) ( .Net General | General )
my Visual Source Safe database had grown very large and i couldn't see why.  digging around in the aaaaaaamb.a files revealed an episode of robin-hood that had accidentally been checked in to VSS.  the thing was i had deleted it through VS but it hadn't been purged from VSS.  with the VSS graphical interface, you can right-click any folder and it will tell you in the deleted items tab if there are any deleted (but not yet purged) items.  however this is very time-consuming.
thanks to a post on a newsgroup, i discovered the command line interface, which has an option to list deleted files, and you can then purge them. you still have to scan through the output, which is presented in a very crude way to say the least.  if there are deleted files, you would think it should just list them.  but no, it lists every directory and says 'no items found under ...' after it, which makes for a lot of noise when you are trying to scan for directories that actually contain deleted files. 
anway, here's the commands:

set SSDIR=C:\Data\VSS                                        ** the folder containing of your srcsafe.ini file **
cd "C:\Program Files\Microsoft Visual SourceSafe\"
ss dir -R -D $/*.*

Comments [0] | | # 
# Monday, January 15, 2007
Monday, January 15, 2007 12:33:47 PM (GMT Standard Time, UTC+00:00) ( .Net General | Database )
if you're querying an MS access database from within access, you can use ? and # as wildcard single character/digit placeholders.
however, if you're querying via OleDb, then you have to use _
this took me ages to figure out. thanks to this useful entry in msdn2.

Comments [0] | | # 
# Tuesday, October 10, 2006
Tuesday, October 10, 2006 4:55:31 PM (GMT Daylight Time, UTC+01:00) ( .Net General )
if i ever forget how to write this function then... well, let's hope i never do.  i was really surprised not to be able to find it in the SDK.  i'm posting it here for reference anyway.  note: this can only be used in .Net 2.0 which supports generics.
/// <summary>
/// Swap 2 objects
/// </summary>
public static void Swap<T>(ref T first, ref T second)
{
T tmp = first;
first = second;
second = tmp;
}

Comments [3] | | # 
# Saturday, October 07, 2006
Saturday, October 07, 2006 1:32:30 AM (GMT Daylight Time, UTC+01:00) ( .Net General | Asp.Net | Windows Server 2003 )
i found this insanely difficult because none of the supposedly normal options worked.  merge modules didn't seem suitable because i just wanted to install the CR dlls once and for all on the server.  according to an MSDN2 article i should be able to perform a windows installer deployment, but of course when i copy the key code from the VS2005 > Help > About dialog, and use it in the installer on the server, the error message is that the key code has expired or is invalid.  'business objects' appear to be forcing users into an upgrade path for their new product, by claiming that a 'compatibility upgrade' is required for VS 2005, which of course you have to fork out for.  such a load of crap. 
anyway, what eventually worked for me was to go back into my VS 2005 dev machine and create a web set up project. go into the project properties and click the prerequisites button, select CR for .Net 2.0 and then just build the empty setup project. if you look in your output folder, there is an installation file called CRRedist2005_x86.msi.  Just whack this onto the server and your crystal reports should run fine, i didn't need to reboot or restart IIS.  Note there is no also need to copy any of the CR dlls to your web site bin folder. 

yet another miserable failure for crystal reports!

Comments [1] | | # 
# Wednesday, August 16, 2006
Wednesday, August 16, 2006 4:39:04 PM (GMT Daylight Time, UTC+01:00) ( .Net General | Asp.Net | General )
i took this off Wikipedia and trimmed it down to plain text for a database.  just posting it here for reference:

You may be more interested in the ISO official list of countries and their 2-digit codes.

Abkhazia
Afghanistan
SBA Akrotiri and Dhekelia
Ã…land
Albania
Algeria
American Samoa
Andorra
Angola
Anguilla
Antigua and Barbuda
Argentina
Armenia
Aruba
Saint Helena Ascension Island
Australia
Austria
Azerbaijan
The Bahamas
Bahrain
Bangladesh
Barbados
Belarus
Belgium
Belize
Benin
Bermuda
Bhutan
Bolivia
Bosnia and Herzegovina
Botswana
Brazil
Brunei
Bulgaria
Burkina Faso
Burundi
Cambodia
Cameroon
Canada
Cape Verde
Cayman Islands
Central African Republic
Chad
Chile
China
Christmas Island
Cocos (Keeling) Islands
Colombia
Comoros
Congo
Cook Islands
Costa Rica
Côte d'Ivoire
Croatia
Cuba
Cyprus
Czech Republic
Denmark
Djibouti
Dominica
Dominican
Ecuador
Egypt
El Salvador
Equatorial
Eritrea
Estonia
Ethiopia
Falkland
Faroe Islands
Fiji
Finland
France
French Polynesia
Gabon
Gambia
Georgia
Germany
Ghana
Gibraltar
Greece
Greenland
Grenada
Guam
Guatemala
Guernsey
Guinea
Guinea-Bissau
Guyana
Haiti
Honduras
Hong Kong
Hungary
Iceland
India
Indonesia
Iran
Iraq
Ireland
Isle of Man
Israel
Italy
Jamaica
Japan
Jersey
Jordan
Kazakhstan
Kenya
Kiribati
North Korea
South Korea
Kosovo
Kuwait
Kyrgyzstan
Laos
Latvia
Lebanon
Lesotho
Liberia
Libya
Liechtenstein
Lithuania
Luxembourg
Macau
Macedonia
Madagascar
Malawi
Malaysia
Maldives
Mali
Malta
Marshall Islands
Mauritania
Mauritius
Mayotte
Mexico
Micronesia
Moldova
Monaco
Mongolia
Montenegro
Montserrat
Morocco
Mozambique
Myanmar
Nagorno-Karabakh
Namibia
Nauru
Nepal
Netherlands
Netherlands Antilles
New Caledonia
New Zealand
Nicaragua
Niger
Nigeria
Niue
Norfolk Island
Northern Cyprus
Northern Mariana Islands
Norway
Oman
Pakistan
Palau
Palestinian
Panama
Papua New Guinea
Paraguay
Peru
Philippines
Pitcairn Islands
Poland
Portugal
Pridnestrovian Moldavian Republic
Puerto Rico
Qatar
Romania
Russia
Rwanda
Saint Helena
Saint Kitts and Nevis
Saint Lucia
Saint Pierre and Miquelon
Saint Vincent and the Grenadines
Samoa
San Marino
São Tomé and Príncipe
Saudi Arabia
Senegal
Serbia
Seychelles
Sierra Leone
Singapore
Slovakia
Slovenia
Solomon Islands
Somalia
Somaliland
South Africa
South Ossetia
Spain
Sri Lanka
Sudan
Suriname
Svalbard
Swaziland
Sweden
Switzerland
Syria
Tajikistan
Tanzania
Thailand
East Timor
Togo
Tokelau
Tonga
Trinidad and Tobago
Tristan da Cunha
Tunisia
Turkey
Turkmenistan
Turks and Caicos Islands
Tuvalu
Uganda
Ukraine
United Arab Emirates
United Kingdom
United States
Uruguay
Uzbekistan
Vanuatu
Vatican City
Venezuela
Vietnam
Virgin Islands (British)
Virgin Islands (United States)
Wallis and Futuna
Western Sahara
Yemen
Zambia
Zimbabwe

Comments [5] | | # 
# Friday, August 11, 2006
Friday, August 11, 2006 1:20:57 PM (GMT Daylight Time, UTC+01:00) ( .Net General | .Net Windows Forms | Asp.Net )
I have a few reports in my web application that i export in PDF format only.  You would think this would remove any printer driver complications but apparently not.
Crystal Reports have a surprisingly good article to help troubleshoot issues with printing or exporting a report.

The problem i was finding was that in a Text object or Unbound field, the text would be truncated at the edge of the box boundary.  Some users have reported strange mid-word wrapping, but mine was a different problem, actually hacking off the end of each line.  This didn't happen on my dev machine, but it did happen on the server (2003).  When i compared them side by side, the Times New Roman font on the server was stretched by about 5% compared to my dev machine.
with the help of the CR article, i tracked it down to the lack of any printer installed on the server, i had also disabled the Print Spooler service which was preventing even the Microsoft Office Document Imaging Printer from being available.  So i started the print spooler service and made sure that the Office Imaging Printer was marked as the default printer.  Then i went back to Visual Studio and set all the reports to use the Office Imaging printer.  This solved the problem. 

You could of course install a PDF only printer such as PrimoPDF if you didn't have MS office installed on the server. As long as the server has the same printer installed as the one set in the report design, everything turns out fine.

Comments [1] | | # 
# Thursday, August 10, 2006
Thursday, August 10, 2006 5:31:20 PM (GMT Daylight Time, UTC+01:00) ( .Net General | .Net Windows Forms | Asp.Net )
I was getting blank fields in my crystal report if one of the values in the formula was null.
Thanks to this post i know better than to trust Crystal's crummy concatenation rules.  Here is what Gundula Wangerin had to say:
A string concatenated with null always gives null.
You have to check if the first name is null with
     if not IsNull(firstname) then 
        lastname + " " + firstname
    else
        lastname

Comments [3] | | # 
# Friday, August 04, 2006
Friday, August 04, 2006 11:26:38 AM (GMT Daylight Time, UTC+01:00) ( .Net General | Asp.Net | Windows Server 2003 )
I picked up a tip on the Internet to display a "No records" message if the report contains no records, rather than leaving the user with an empty screen.  Simply Format the text field and enter a formula next to 'Suppress' with something like
Count({Table.Field}) > 0
This will suppress (hide) the message if there are records in the report.

I tried using a similar approach to hide a text field when there are no records, so it is essentially the same thing in reverse.  You would think a simple formula on the 'suppress' property this would achieve the desired effect:
Count({Table.Field}) = 0

But apparently the value returned by Count can be null if there are no records.  so you have to use:

IsNull(Count({Table.Field})) OR Count({Table.Field}) = 0

This is just another crystal reports annoyance, of which there are many.

Comments [9] | | # 
# Wednesday, August 02, 2006
Wednesday, August 02, 2006 8:45:07 PM (GMT Daylight Time, UTC+01:00) ( .Net General | Asp.Net | Windows Server 2003 )
i'm part of the dd/MM/yyyy world, and that often means running into problems when using software developed with MM/dd/yyyy defaults, such as crystal reports. 
i have all the report options set to use the system defaults, which are regionally set to ireland in windows, but that isn't enough, the dates still come out in MM/dd/yyyy format.  i have the date + date/time fields customised within crystal reports to dd/MM/yyyy but that isn't enough either.  with lots of hours googling and no answers that worked, i resorted to searching the registry and found that some of the user accounts were still using US regional settings.  If you look in HKEY_USERS > you see a list of all the account IDs on the computer.  My guess is that crystal reports must use the SYSTEM account or another non-interactive account, and it takes the regional settings from there.  So if you search for sShortDate in the registry, you will find all the appropriate settings and can replace the MM/dd/yyyy values with your preferred format.
i had to reboot to get it to take effect. 

Comments [12] | | # 
# Friday, March 03, 2006
Friday, March 03, 2006 3:43:20 PM (GMT Standard Time, UTC+00:00) ( .Net General | Asp.Net )
I wanted to install my web application assembly into the GAC, but this is made more complicated by the multitude of assemblies produced by VS when i publish the web site.  On the newsgroups, i found some talk of a tool called Merge_Aspnet.exe but i couldn't find it anywhere.  Eventually i found it as a download on MSDN, it is bundled as part of the Web Deployment Projects.  You install it, and then right-click your project in VS and you should see a new menu item "Add Web Deployment Project".  I am baffled as to why they didn't just add a new project type in the list of projects under "Deployment".  There is a link to "Search online templates" so i think it should really be available there.  but it looks like MS did a hack just to add in a new item to the project context menu.  but it works... so i'll stop complaining. 

Comments [0] | | # 
# Friday, February 17, 2006
Friday, February 17, 2006 6:09:05 PM (GMT Standard Time, UTC+00:00) ( .Net General | .Net Windows Forms )
Screenshot of upload operationhi,
i was delighted to see .Net 2.0 has an excellent class for working with FTP.  looking back i can't believe it wasn't part of 1.1. 

i noticed there was no built-in support for reporting the progress of an FTP operation to a user interface.  I needed this functionality in an app i'm working on so i built a class and i'm posting it here in case anyone is interested.

The class FtpProgress inherits from BackgroundWorker, so you just drag an FtpProgress component onto your form, hook up events for Completed and ProgressChanged, and your laughing.  There is a mini class called FtpSettings which stores all the connect info etc to pass in as a paramter to the RunWorkerAsync() method.

I have a variable called 'ChunkSize' which determines the size of each buffer written to the FtpWebRequest output stream.  It is set to 4k by default, and you can increase this if you want for very fast networks but i don't think it will make much difference.  I think the overhead is negligible.

Download the source code and demo application [30 k].

It only does upload, so if you want download functionality, you can do the mirror-image of the upload code, i.e. instead of writing buffers to the output stream, read it from the input stream, or however that would work :) 

here is all the code you need to start an Upload, and report progress:
private void btnUpload_Click(object sender, EventArgs e)
{
// create a new FtpSettings class to store all the paramaters for the FtpProgress thread
FtpSettings f = new FtpSettings();
f.Host = this.txtHost.Text;
f.Username = this.txtUsername.Text;
f.Password = this.txtPassword.Text;
f.TargetFolder = this.txtDir.Text;
f.SourceFile = this.txtUploadFile.Text;
f.Passive = this.chkPassive.Checked;
f.Port = Int32.Parse(this.txtPort.Text);
this.ftpProgress1.RunWorkerAsync(f);
}

private void ftpProgress1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.toolStripStatusLabel1.Text = e.UserState.ToString(); // the message will be something like: 45 Kb / 102.12 Mb
this.toolStripProgressBar1.Value = Math.Min(this.toolStripProgressBar1.Maximum, e.ProgressPercentage);
}
Comments [29] | | # 
# Thursday, February 02, 2006
Thursday, February 02, 2006 1:38:34 PM (GMT Standard Time, UTC+00:00) ( .Net General | .Net Windows Forms | Asp.Net | Windows Server 2003 )
For some reason i can't get SelectNodes to work with the IIS 6 MetaBase.xml file.  It just doesn't return any matches.  I think the structure may be non-standard or too complex, or else i'm not using it right.  but in any case, I worked around the problem by opening the file with File.OpenText and using Regex to parse out the nodes i'm interested in, namely the IISWebServer nodes.

string MetaBase = "";
using(StreamReader sr = File.OpenText(ConfigurationManager.AppSettings["MetaBasePath"].ToString()))
MetaBase = sr.ReadToEnd();

foreach(Match m in new Regex(@"(?x:)<IIsWebServer[^>]*>.*?</IIsWebServer>", RegexOptions.IgnoreCase).Matches(MetaBase))
{
XmlDocument doc = new XmlDocument();
string IisCode = "", ServerBindings = "", ServerComment = "";
doc.LoadXml(m.Groups[0].Value);
XmlElement node = doc.DocumentElement;
// now you can access the node attributes
...

Comments [0] | | # 
# Monday, January 30, 2006
Monday, January 30, 2006 10:53:44 PM (GMT Standard Time, UTC+00:00) ( .Net General | Asp.Net )
i kept getting these sporadic error messages on my web applications and i could never figure out why. 
Unable to validate data at
System.Web.Configuration.MachineKey.GetDecodedData(Byte[] buf, Byte[] modifier, Int32 start, Int32 length, Int32& dataLength) at
System.Web.UI.LosFormatter.Deserialize(String input)
searching the net just reveals a troop of similarly frustrated users without solutions.  but today i found the actual reason why, and the work-around.
this excellent post on experts-exchange has the answer.  apparently it happens when users leave a page open for a long time, and then cause a post back.  something to do with the machine key being automatically generated and it changes before the user causes the postback, and it can't validate it then. 
the fix is to set a static machine key. 
There is a kb article with some code to generate a key for you, + instructions.  

Sorted.

Comments [13] | | # 
Monday, January 30, 2006 3:30:12 PM (GMT Standard Time, UTC+00:00) ( .Net General | Asp.Net | Database )
a web site i'm working on imports excel documents, and does some processing on them for importing into a database.  I use the code from this post to do the importing, and it works nicely.  I recently came across a problem where i was encountering duplicate records, and it took me ages to figure out why.  Apparently a 'named range' of cells in a worksheet is treated as a Table by ADOX.  so you get more than you bargain for when you iterate through the tables in the resulting DataSet. 
I was able to work around the problem by discarding any tables that do NOT end in the dollar $ character.

Comments [1] | | # 
# Wednesday, January 25, 2006
Wednesday, January 25, 2006 1:52:05 PM (GMT Standard Time, UTC+00:00) ( .Net General | .Net Windows Forms | Asp.Net )
I have a text object in my crystal report, and i set the value programatically for it, using something like this:
(rpt.Section3.ReportObjects["txtDate"] as TextObject).Text = DateTime.Now.ToShortDateString();
this works fine, until you put a string with line breaks inside it, specifically \r\n or the carriage return character.  This is a bug in CR for .Net, you can read the official blurb here.  the work-around code they post is in VB, and it made no sense to me when i read it.
what they actually do is make you change the TextObject into a FormulaField (you have to view the 'Field Explorer' tab next to the toolbox, and drag on a Formula Field).  Then you set 'CanGrow' to true, and then you go back to your code, and do the most arcane work-around i've ever seen.  you set the formula to your text string, but you must surround it in single quote characters, and replace \r\n with some inline managed code as follows: ' + chr(10) + '
when i first read this, i thought they had made a syntax error, but this is the way to do it, and it works.  the new format for setting the formula field is:
rpt.DataDefinition.FormulaFields[0].Text = "'" + YourString.Replace("\r\n", "' + chr(10) + '") + "'";

Comments [16] | | # 
# Monday, January 09, 2006
Monday, January 09, 2006 5:57:34 PM (GMT Standard Time, UTC+00:00) ( .Net General | .Net Windows Forms | Asp.Net )
I ran into a frustrating problem today where some properties of my objects weren't getting serialized.  I noticed it was the ones without 'set' accessors.  I didn't want to put set accessors in because the properties should be read-only, but apparently the xml serializer needs the set accessor to de-serialize. 
there is a good discussion on it on this thread.
Luckily we can use the ReadOnlyAttribute setting on the property to prevent it being modified by a programmer at design time.
or you can use the Soap serializer instead of xml, whatever that is.

Comments [0] | | # 
# Thursday, December 29, 2005
Thursday, December 29, 2005 8:34:15 PM (GMT Standard Time, UTC+00:00) ( .Net General | .Net Windows Forms | Asp.Net )
just posting my code-project article on http chunking web services with MTOM here for reference.  by the way it has been updated on codeproject...

Screenshot of windows forms client, uploading a file

Introduction

In trying to keep up to speed with .NET 2.0, I decided to do a .NET 2.0 version of my CodeProject article "DIME Buffered Upload" which used the DIME standard to transfer binary data over web services. The DIME approach was reasonably efficient but the code is quite complex and I was keen to explore what .NET 2.0 has to offer. In this article, I use version 3.0 of the WSE (Web Service Enhancements) which is available for .NET 2.0 as an add-in, to provide a simpler and faster method of sending binary data in small chunks over HTTP web services.

Background

Just a recap on why you may need to send data in small chunks at all: if you have a large file and you want to send it across a web service, you must understand the way it all fits together between IIS, .NET, and the web service call. You send your file as an array of bytes, as a parameter to a web service call, which is all sent to the IIS web server as a single request. This is bad if the size of the file is beyond the configured MaxRequestLength of your application, or if the request causes an IIS timeout. It is also bad from the point of view of providing feedback of the file transfer to the user interface because you have no indication how the transfer is going, until it is either completed or failed. The solution outlined here is to send chunks of the file one by one, and append them to the file on the server.

There is an MD5 file hash done on the client and the server to verify that the file received is identical to the file sent.

Also, there is an upload and download code included in this article.

Adventures with MTOM

MTOM stands for SOAP "Message Transmission Optimization Mechanism" and it is a W3C standard. To use it (and to run this application), you must download and install WSE 3.0, which includes MTOM support for the first time. If you look in the app.config and web.config files in the source code, you will see sections referring to the WSE 3 assembly, and a messaging clientMode or serverMode setting. These are necessary to run MTOM in the application.

The problem with DIME was that the binary content of the message was sent outside the SoapEnvelope of the XML message. This meant that although your message was secure, the Dime Attachment may not be secure. MTOM fully complies with the other WS-* specifications (like WS-Security) so the entire message is secure.

It took me a while to realise that when MTOM is turned on for the client and the server, WSE automatically handles the binary encoding of the data in the web service message. With DIME and WSE 2.0, you had to configure your app for DIME and then use DimeAttachments in your code. This is no longer necessary, you just send your byte[] as a parameter or return value, and WSE makes sure that it is sent as binary, and not padded by XML serialization as it would be in the absence of DIME or MTOM.

How it works

The web service has two main methods, AppendChunk is for uploading a file to the server, DownloadChunk is for downloading from the server. These methods receive parameters for the file name, the offset of the chunk, and the size of the buffer being sent/received.

The Windows Forms client application can upload a file by sending all the chunks one after the other using AppendChunk, until the file has been completely sent. It can do an MD5 hash on the local file, and compare it with the hash on the file on the server, to make sure the contents of the files are identical. The download code is very similar, the main difference is that the client must know from the server how big the file is, so that it can know when to stop requesting chunks.

A simplified version of the upload code is shown below (from the WinForms client). Have a look in the code for Form1.cs to see the inline comments + the explanation of the code. Essentially, a file stream is opened on the client for the duration of the transfer. Then the first chunk is read into the Buffer byte array. The while loop keeps running until the FileStream.Read() method returns 0, i.e. the end of the file has been reached. For each iteration, the buffer is sent directly to the web service as a byte[]. The 'SentBytes' variable is used to report progress to the form.

using(FileStream fs = new FileStream(LocalFilePath, FileMode.Open, FileAccess.Read))
{
int BytesRead = fs.Read(Buffer, 0, ChunkSize);
while(BytesRead > 0 && !worker.CancellationPending)
{
ws.AppendChunk(FileName, Buffer, SentBytes, BytesRead);
SentBytes += BytesRead;
BytesRead = fs.Read(Buffer, 0, ChunkSize);
}
}

Example of the BackgroundWorker class in .NET 2.0

.NET 2.0 has a great new class called 'BackgroundWorker' to simplify running tasks asynchronously. Although this application sends the file in small chunks, even these small chunks would delay the WinForms application and make it look crashed during the transfer. So the web service calls still need to be done asynchronously. The BackgroundWorker class works using an event model, where you have code sections to run for DoWork (when you start), ProgressChanged (to update your progress bar / status bar), and Completed (or failed). You can pass parameters to the DoWork method, which you could not do with the Thread class in .NET 1.1 (I know you could with delegates, but delegates aren't great for thread control). You can also access the return value of DoWork in the Completed event handler. So for once, MS has thought of everything and made a very clean threading model. Exceptions are handled internally and you can access them in the Completed method via the RunWorkerCompletedEventArgs.Error property.

The code shown below is an example of the ProgressChanged event handler:

private void workerUpload_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// update the progress bar and status bar text
this.toolStripProgressBar1.Value = e.ProgressPercentage;
this.statusText.Text = e.UserState.ToString();
// summary text is sent in the UserState parameter
}

I have used four BackgroundWorker objects in the application:

  • one to manage the upload process,
  • one to manage the download process,
  • another to calculate the local MD5 file hash in parallel while waiting for the server result,
  • and another to download the list of files in the Upload server folder to allow the user to select a file to download.

The reason I use a BackgroundWorker object for each task is because the code for each task is tied in to the events for that object.

A good example of Thread.Join()

When the upload or download is complete, the client asks for an MD5 hash of the file on the server, so it can compare it with the local file to make sure they are identical. I originally did these in sequence. But it can take a few seconds to calculate the result for a large file (anything over a few hundred MB), so the application was waiting five seconds for the server to calculate the hash, and then five more seconds for the client to calculate its own hash. This made no sense, so I decided to implement a multi-threaded approach to allow them to run in parallel. While the client is waiting on the server, it should be calculating its own file hash. This is done with the Thread class, and the use of the Join() method which blocks execution until the thread is complete.

The code below shows how this is accomplished:

// start calculating the local hash (stored in class variable)
this.hashThread = new Thread(new ThreadStart(this.CheckFileHash));
this.hashThread.Start();

// request the server hash
string ServerFileHash = ws.CheckFileHash(FileName);

// wait for the local hash to complete
this.hashThread.Join();

if(this.LocalFileHash == ServerFileHash)
e.Result = "Hashes match exactly";
else
e.Result = "Hashes do not match";

There is a good chance that the two operations will finish at approximately the same time, so very little waiting around will actually happen.

Performance compared with DIME

I found that MTOM was about 10% faster than DIME in my limited testing. This is probably to do with the need to package up each chunk into a DIME attachment, which is no longer necessary with MTOM. I was able to upload files of several gigabytes in size without problems.

Obviously, there is an overhead with all this business of reading file chunks and appending them, so the larger the chunk size, the more efficient your application will be. It should be customised based on the network and the expected size of files. For very small files, it is no harm to use small chunk sizes (e.g., 32 Kb) because this will give accurate and regular feedback to the user interface. For very large files on a fast network, consider using 4000 Kb to make good use of the bandwidth and reduce the File Input/Output overhead. If you want to send chunks larger than 4 MB, you must increase the .NET 2.0 Max Request Size limit in your web.config.

Conclusions

Feel free to use this code and modify it as you please. Please post a comment for any bugs, suggestions, or improvements. Enjoy!

Comments [24] | | # 
# Thursday, December 15, 2005
Thursday, December 15, 2005 1:28:01 PM (GMT Standard Time, UTC+00:00) ( .Net General | Asp.Net | Database )
i was experimenting with how .Net 2.0 does xml serialization of objects, and i got it serializing nicely with the following code. 
XmlSerializer xs = new XmlSerializer(typeof(PageCollection));
xs.Serialize(fs, pages); // 'fs' is a FileStream to my xml file, and 'pages' is a collection class of objects

the problem was when i tried to deserialize it, like so:
XmlSerializer xs = new XmlSerializer(typeof(PageCollection));
pages = xs.Deserialize(fs) as PageCollection;

i got this error:

xmlns=''> was not expected

i found this post on google which described the same errors, and a workaround (by adding an empty namespace) but it didn't work for me.  perhaps it is a difference in the serialization process with with .Net 2.0.  what fixed it for me was setting an XmlRootAttribute for the class i was serializing. like so:
[XmlRootAttribute("CmsPages", Namespace = "http://www.whatever.com/Cms", IsNullable = false)]
hope this helps someone else out there with the same problem.
Comments [0] | | # 
# Friday, December 02, 2005
Friday, December 02, 2005 12:26:22 PM (GMT Standard Time, UTC+00:00) ( .Net General | .Net Windows Forms | Asp.Net | Database )

I had a transaction comprised of about 5 commands, and in the middle of it, i needed to do a SELECT on a table to know what values to insert for one of the commands.  I encountered a timeout just after i called Fill on the DataAdapter.  it took me a while to figure out that it was because the transaction had already taken out a lock on the table i was selecting from.  makes sense now, but i spent ages on it!  just posting it here in case anyone else runs into the same problem.

Comments [0] | | # 
# Wednesday, November 23, 2005
Wednesday, November 23, 2005 3:40:36 PM (GMT Standard Time, UTC+00:00) ( .Net General )

Introduction

I've spent a long time trying many different approaches at getting rid of MS Word HTML, when importing or pasting text into my content management system, with very mixed success.  Previous efforts involved using the MSHTML Element Dom but this was slow and difficult to implement.  i think i've finally found a satisfactory and fast solution using only regular expressions.  Please feel free to use it in your applications, and post any improvements you may find.

The Code

/// <summary>
/// Removes all FONT and SPAN tags, and all Class and Style attributes.
/// Designed to get rid of non-standard Microsoft Word HTML tags.
/// </summary>
private string CleanHtml(string html)
{
// start by completely removing all unwanted tags
html = Regex.Replace(html, @"<[/]?(font|span|xml|del|ins|[ovwxp]:\w+)[^>]*?>", "", RegexOptions.IgnoreCase);
// then run another pass over the html (twice), removing unwanted attributes
html = Regex.Replace(html, @"<([^>]*)(?:class|lang|style|size|face|[ovwxp]:\w+)=(?:'[^']*'|""[^""]*""|[^\s>]+)([^>]*)>","<$1$2>", RegexOptions.IgnoreCase);
html = Regex.Replace(html, @"<([^>]*)(?:class|lang|style|size|face|[ovwxp]:\w+)=(?:'[^']*'|""[^""]*""|[^\s>]+)([^>]*)>","<$1$2>", RegexOptions.IgnoreCase);
return html;
}

Samples of non-standard Microsoft Word HTML

<SPAN lang=EN-IE style="mso-ansi-language: EN-IE">
<p class="MSO Normal">
<UL style="MARGIN-TOP: 0cm" type=circle>
<o:p>&nbsp;</o:p>
<li class=MsoNormal style='mso-list:l3 level1 lfo3;tab-stops:list 36.0pt'>

Explanation of Regular Expressions

I've spent a good deal of time examining the problematic tags that MS Word inserts in its HTML, some examples are shown above.  The above code is based on a few requirements for my CMS:

  • remove all FONT and SPAN tags, because all the content in my CMS is done through style-sheets.
  • remove all CLASS and STYLE tags because they mean nothing outside of the original word document
  • remove all namespace tags and attributes like <o:p> and < ... v:shape ... >

The first regular expression removes unwanted tags, and is broken down as follows:

<[/]?(font|span|xml|del|ins|[ovwxp]:\w+)[^>]*?>
  • match an open tag character <
  • and optionally match a close tag sequence </  (because we also want to remove the closing tags)
  • match any of the list of unwanted tags: font,span,xml,del,ins
  • a pattern is given to match any of the namespace tags, anything beginning with o,v,w,x,p, followed by a : followed by another word
  • match any attributes as far as the closing tag character >
  • the replace string for this regex is "", which will completely remove the instances of any matching tags.
  • note that we are not removing anything between the tags, just the tags themselves

The second regular expression removes unwanted attributes, and is broken down as follows:

<([^>]*)(?:class|lang|style|size|face|[ovwxp]:\w+)=(?:'[^']*'|""[^""]*""|[^\s>]+)([^>]*)>
  • match an open tag character <
  • capture any text before the unwanted attribute (This is $1 in the replace expression)
  • match (but don't capture) any of the unwanted attributes: class, lang, style, size, face, o:p, v:shape etc.
  • there should always be an = character after the attribute name
  • match the value of the attribute by identifying the delimiters. these can be single quotes, or double quotes, or no quotes at all.
  • for single quotes, the pattern is: ' followed by anything but a ' followed by a '
  • similarly for double quotes. 
  • for a non-delimited attribute value, i specify the pattern as anything except the closing tag character >
  • lastly, capture whatever comes after the unwanted attribute in ([^>]*)
  • the replacement string <$1$2> reconstructs the tag without the unwanted attribute found in the middle.
  • note: this only removes one occurence of an unwanted attribute, this is why i run the same regex twice.  For example, take the html fragment: <p class="MSO Normal" style="Margin-TOP:3em"> 
    the regex will only remove one of these attributes.  Running the regex twice will remove the second one.  I can't think of any reasonable cases where it would need to be run more than that.

Suggestions!

If you have any suggestions or improvments, please post them here as comments.
Thanks :)

p.s. thanks to BinBin for the fix to preserve attributes like 'align=center'.
Comments [27] | | # 
# Wednesday, September 28, 2005
Wednesday, September 28, 2005 11:01:17 AM (GMT Daylight Time, UTC+01:00) ( .Net General | Database )

This might sound really obvious, but i couldn't find a better way.  Normally i would use TOP in the SQL query to limit the number of records i want to retrieve, but in my case, this value is parameterised and Access won't allow me to parameterise that value.  I tried using a DataView but TOP isn't one of it's supported functions.  So i just loop through the dataset and keep removing rows until the right number of records is reached.

int maxItems = 5;
while(ds.Tables[0].Rows.Count > MaxItems)
    ds.Tables[0].Rows.RemoveAt(MaxItems);
Comments [0] | | # 
# Tuesday, March 15, 2005
Tuesday, March 15, 2005 6:26:27 PM (GMT Standard Time, UTC+00:00) ( .Net General )
HowTo: get a random letter in C#
Comments [2] | | # 
# Tuesday, March 01, 2005
Tuesday, March 01, 2005 6:46:28 PM (GMT Standard Time, UTC+00:00) ( .Net General )
Regex for a strong password
Comments [0] | | # 
# Monday, November 22, 2004
Monday, November 22, 2004 4:53:35 PM (GMT Standard Time, UTC+00:00) ( .Net General )
Using custom datatypes in a .Net Dataset
Comments [0] | | # 
# Sunday, September 19, 2004
Sunday, September 19, 2004 11:32:44 PM (GMT Daylight Time, UTC+01:00) ( .Net General )
Problems using Visual Studio.Net and Visual Source Safe with Web Projects
Comments [0] | | # 
# Thursday, July 29, 2004
Thursday, July 29, 2004 11:29:17 AM (GMT Daylight Time, UTC+01:00) ( .Net General )
Controlling a dial-up connection via a web service
Comments [4] | | # 
# Wednesday, June 23, 2004
Wednesday, June 23, 2004 1:08:19 PM (GMT Daylight Time, UTC+01:00) ( .Net General )
WSE 2.0 send DIME attachments from client TO a web service
Comments [1] | | #