.Net ramblings
# Thursday, 29 July 2004
Controlling a dial-up connection via a web service


I just finished writing a simple client/server .Net application that allows windows PCs on a LAN to control the dial-up connection on a web server. 
I'm currently using it at home on a small ad-hoc wireless network because the windows Internet Connection Sharing setting for 'dial on-demand' is very unreliable and it dials up when you don't want it to.  This way you control when it connects and disconnects. Also, i was using terminal services to connect and disconnect which is way overkill!

I have put the source code and executables online for anyone to download, use modify etc. Download: RemoteDialer.zip (94 k).  There is a readme file in there, but here are the instructions anyway which explain how it works and how to set it up.

How it works

The web server has methods for Dial() and Hangup().  They each call a batch file with Process.Start, which is configured to output the result of the operation to a text file.  The batch files use the rasdial.exe tool to connect and disconnect the connection. The web service reads the text file before sending back the contents of it, to the client that requested the operation.

The desktop application for client PCs has a system tray icon, with a right-click menu to Start or Stop the connection.

There is also a web page to connect/disconnect, so you can allow non-windows users (who obviously couldn't run the desktop app) who are on your network to control the connection. To go to this page, just go to the address of the v.dir on the server.


To configure the web server:

 - set up a new application pool which runs as Local Service
 - copy the 'Web' folder to the web server
 - make it a virtual directory
 - set the AppPool for the v.dir to the appPool you just created
 - Copy the 2 batch files in the 'Batch Files' folder to the server.
 - It's easiest to put them in the c:\ root, otherwise you can
   update the web.config application keys accordingly.

To configure the windows PCs:

 - Locate the RemoteDialer.exe.config file in \WinClient\bin
 - Adjust the http value key to match the server and v.dir name on the
 - Copy the WinClient\Bin folder to each PC
 - Run the .exe and you will see a new icon the system tray.
 - Right-click the tray icon to start or stop the dial up connection
 - The status of the operation is outputted to a pop up box.

Note: The reason for the new Application Pool is so that the web service
can interact with the system and write the output of operations
to text files.


If you have any comments or queries on it, or if there are bugs or problems, email me.  tim@mackeyNO_SPAM_PLEASE.ie

Thursday, 29 July 2004 11:29:17 (GMT Daylight Time, UTC+01:00)  #    Comments [4]  .Net General

# Monday, 26 July 2004
A great ASP.NET treeview control that is compatible with other browsers

I'm building a web front-end for my content management system (so that i can sell it to clients who don't run 100% windows PCs).  and i needed a cross-platform treeview control.  i did a quick search and noticed that the CrapStuff Treeview listed on asp.net was a popular one (http://crapstuff.craponne.org).  Ignoring the unusual name i tried it out and am delighted with it.  i have used the microsoft.ui.webcontrols treeview before and found it frustrating on several issues, that others have also encountered and have remained unresolved due to MS not supporting the control.

programatically the CrapStuff Treeview is great to work with, and i like the DHTML support also so that the control can be expanded fully without any postbacks, unless you want them to happen.

the author's name is Pascal Craponne so that's where he gets the name i suppose.  it's a great control and he has given it out for free.  thank you Pascal!

Monday, 26 July 2004 23:05:05 (GMT Daylight Time, UTC+01:00)  #    Comments [1]  Asp.Net

# Friday, 23 July 2004
Outlook 2003 blocking attachments, kb fix didn't work

someone sent me a .mdb attachment today and i couldn't open it in outlook. no amount of searching the options could change this behaviour. i searched online and found a few microsoft kb articles about it, and how to change the registry to add exemptions to the 'Level 1' list of file extensions, which outlook regards as unsafe.  i did all the steps in the article but it still didn't work.  then i found this GREAT outlook plug in which adds a tab to the outlook options and allows you to configure how outlook handles all the attachments.


How on earth MS forgot about this is beyond me.  they actually suggest that you ask the person who sent it to rename the file with a bogus extension and resend the attachment.  surely it is sufficient to force users to save the file to disk and then open it themselves by choice.  sheesh!

Friday, 23 July 2004 17:51:56 (GMT Daylight Time, UTC+01:00)  #    Comments [0]  General

# Wednesday, 21 July 2004
Some issues with PureComponents Navigator control 2.0

I have built a content management system in windows forms, and i set it up with individual forms to manage each of the aspects to the system. one to manage users, another for templates etc.  however this made the user interface quite cluttered sometimes with several windows open.  so i decided to make my app work like outlook 2003 with its nice control on the left with buttons and side panels on the left, and the main panel on the right for whichever button is selected. 

i chose the pure components navigator control (2.0) and i'm testing it at the moment.  i don't like the way the developers don't give a public support forum, they say it shouldn't need support, but i have had several issues with it.

the main problem i have is that the control seems to crash Visual Studio (2003 enterprise edition) when i add panels to each group and set properties such as anchoring in the designer.  i ended up cutting the code to set up the panels from InitialiseComponent and pasting it into a separate function that i call in the constructor.

also, the group class is supposed to have a FitPanel() method, but it doesn't fill the panel to the size of the space in the control between the buttons.  i tried setting the panel to DockStyle.Fill and it fills the entire control on top of all the other buttons.  I ended up manually creating the panels (right click a group and Add Panel) and then resize it to the desired dimensions, and set the anchor to top|left|bottom|right.  i must say it was quite messy though. 

it seems like a great control and it works quick enough too. i'll need to do more testing before i pay for the license though.

Wednesday, 21 July 2004 23:58:51 (GMT Daylight Time, UTC+01:00)  #    Comments [2]  .Net Windows Forms

# Monday, 12 July 2004
System tray icons in Windows forms apps

If you add a NotifyIcon (system tray icon) to a Windows forms application, you will need to modify the dispose event of the form containing the icon, to explicitly call dispose on the notifyIcon as follows:

protected override void Dispose( bool disposing )
 if( disposing )
  if (components != null)
 base.Dispose( disposing );

if you don't do this, the icon will get left in the system tray after the application quits, until you move the mouse over the icon and then it will disappear.

Monday, 12 July 2004 10:02:42 (GMT Daylight Time, UTC+01:00)  #    Comments [0]  .Net Windows Forms

# Thursday, 24 June 2004
Http Status 400 trying to send large DIME attachments

Testing out my new DIME attachment code with WSE2, i ran into http errors with large uploads.  The error i got was a System.Net.WebException saying that it was a bad request, Http status 400, with an event in the application log under WSE:

HTTP/ASMX Message Receive Failure: Microsoft.Web.Services2.Dime.DimeFormatException: WSE352: The size of the record uuid:c0c3c949-5fb1-4171-88aa-06ce14499e44 exceed its limit.
at Microsoft.Web.Services2.Dime.DimeRecord.UpdateTotalBytesRead...

I had the maxRequestLength set to 128000 in web.config, but i found out that this does not apply to WSE2.  adding the following entry into web.config solved the problem.

<?xml version="1.0" encoding="utf-8" ?>
    <maxRequestLength>256000</maxRequestLength><!-- 256 mb -->

Thursday, 24 June 2004 09:49:24 (GMT Daylight Time, UTC+01:00)  #    Comments [8]  Asp.Net

# Wednesday, 23 June 2004
WSE 2.0 send DIME attachments from client TO a web service

I wanted to improve the performance of my winforms app sending binary data to a web service, so i chose to upgrade to WSE 2.0 and use the DIME specification. 

I checked out the examples in the docs, but only found one with a web service sending an attachment to a client.  i wanted it the other way around.  i tried to modify it to suit my own needs, but couldn't get it to work. (in the end i had the response and request contexts mixed up). I searched high and low online and finally found that there is a sample as part of the WSE full installation which contains just what i wanted.  I could find no mention of this sample in the docs, so I am posting it here.

The code below shows the important methods, on the client and on the server.  It assumes a web project and winforms project both set up for WSE2.0. 

// this is part of a winforms application with an openfiledialog
private void btnUpload_Click(object sender, System.EventArgs e)
  if(this.openFileDialog1.ShowDialog() == DialogResult.OK)
    DimeAttachment dimeAttach = new DimeAttachment("image/gif", TypeFormat.MediaType,this.openFileDialog1.FileName);
    ws = new TestService.Test();
    MessageBox.Show("Upload successful");
    MessageBox.Show("No file selected");

// web service method...
public void ReceiveImage(string filename)
  // Reject any requests which are not valid SOAP requests
  if (RequestSoapContext.Current == null)
    throw new ApplicationException("Only SOAP requests are permitted.");
  if (RequestSoapContext.Current.Attachments.Count == 0)
    throw new ApplicationException("No attachments were sent with the message.");

    FileStream f = System.IO.File.OpenWrite(Server.MapPath("Upload/" + filename));
    Stream s = RequestSoapContext.Current.Attachments[0].Stream;
    byte[] bytes = new byte[s.Length];
    s.Read(bytes, 0, bytes.Length);
    f.Write(bytes, 0, bytes.Length);
  catch(Exception ex)
    throw ex; // or log it...

Wednesday, 23 June 2004 13:08:19 (GMT Daylight Time, UTC+01:00)  #    Comments [1]  .Net General

# Tuesday, 22 June 2004
Adventures with .NET smart clients, running with Local Intranet permissions

I had originally deployed an app with msi but the advantages of smart clients soon began to convince me to change my deployment model.

What you can and can't do in Local Intranet

As a smart client, the app is executed as http://myserver/exec/myApp.exe
This works very nicely, until those security exceptions start appearing.  I had to spend a long time going through my app to find out what was causing the exceptions.  The obvious reason for the exceptions is that programs coming in from the Intranet are less trusted than those on My Computer, and hence are subject to more restrictions by the local security policy.  After much exploration and reading up online, i found out some interesting facts:

  • you can't access Environment.Machinename
  • you can't access My Documents, or UserAppDataPath, etc.
  • you can't call abort() on a Thread, but you can call Sleep() and Start()
  • you can read files through openFileDialog
  • you can read and write to IsolatedStorage (a virtual file-system specially for your app), very useful seeing as you can't write to any physical location on the hard disk by default.
  • you can invoke delegates asynchronously

You could deduce these by looking in the .Net Framework Security Policy (control panel > admin tools > .Net Framework configuration > Runtime security policy > Machine > Permission Sets > Local Intranet), and then double click one of the items in the main pane to the right, to see what restrictions if any are applied for the selected zone.

I found out the above information hard way through trial and error, but it was a very valuable exercise to learn about the security policy.

Using a class library with your smart client app

I got a large part of my app working, but i was using a class library of customised windows forms components, and this was causing a security exception.  It took a few hours to pin down, but the docs finally revealed that the class library must have the following attribute (outside a class declaration):


The app worked fine after I added that line.

Requesting permissions from the local Security Policy before your app runs. 

If you are not sure what zone your app will run in, you can request a minimum level which your app requires. Add the following line of code outside a class declaration in one of the files in your project:

[assembly:PermissionSetAttribute(SecurityAction.RequestMinimum, Name = "LocalIntranet")]

This will alert the user that they don't have permissions to run the app if it is running in a zone less than Local Intranet. 


I personally have found these bits of information very difficult to pull together.  There is a ton of stuff about smart clients but its mostly non-technical.  I even got a CD from msdn about smart clients and i found it useless for helping out with any of the problems i have encountered along the way.

I hear there is a tool called PermCalc.exe coming with Whidbey that will probe an app to see what would cause security problems in a smart client scenario but until then, we are stuck doing it manually :)

Tuesday, 22 June 2004 19:16:34 (GMT Daylight Time, UTC+01:00)  #    Comments [0]  .Net Windows Forms

# Monday, 31 May 2004
Installing Windows XP on a Shuttle ST61G4 with SATA hard disk (no floppy drive)

This is my first post on my new PC, a Shuttle ST61G4.  3Ghz P4, 1Gb ddr 400 ram, 160Gb SATA hard disk.  It feels like a small miracle that it's working, that's how difficult it was for me to install Windows XP on the SATA hard disk.  

Partly it was due to me never having used SATA before, and not realising that Windows XP doesn't bundle the SATA drivers I need for the Silican SATA Controller (Si3112r) that is part of the Shuttle mainboard.  Also, the most annoying part was that Windows setup only allows you to specify additional drivers from a floppy disk.  The shuttle, being a modern compact PC, doesn't come with a floppy of course, so i was stumped.  I really didn't want to pull out the old floppy drive from my other PC, but i was forced to in the end. 

There is a way to slipstream the drivers into the Windows setup files and then burn them back to a CD, but this sounded way too complicated for me. 

The CD with the shuttle mainboard drivers had instructions for how to create the required floppy for a Windows install, and this worked OK, when i finally figured out how to get the Shuttle to recognise the floppy drive.  It took a while to find the plug in the motherboard for the floppy, at the back around the side under the power supply.  Then i had to enable Floppy Disk 1 in the bios as a 1.44mb drive.  Still didn't work, so i enabled Boot up Floppy Seek.  Still didn't work, and i finally found out that another setting 'FDC Controller' was disabled, which i needed to enable.  Then the floppy drive worked, and it went without hitch from there on in.  you also need to enable the Raid controller in the bios. 

When the computer boots, the SATA controller part still says "Drive 1542311Mb - Drive not found" or something, although this doesn't seem to matter since windows is able to recognise it. 

I found all this out the hard way, and i looked for a long time online to find an easier way of doing it, or clear instructions detailing every step necessary, and i found a FAQ page on shuttle.com (afterwards!) which details the process very well, its a shame it was so hard to find through the shuttle web site. 

But my shuttle is fast and very lovely, and it is the quietest thing i have never heard. I can't figure out why they make you press the reset button with a pen though, its so tiny.  In the end, i love it.  

Monday, 31 May 2004 00:20:44 (GMT Daylight Time, UTC+01:00)  #    Comments [2]  General

# Monday, 24 May 2004
Environment.SpecialFolder enumeration not showing up in Visual Studio Intellisense

I was using an OpenFileDialog and i wanted it to open at the My Pictures folder for the current user.  So i looked up the msdn docs for how to locate this folder, and i come across the Environment.SpecialFolder enumeration, which has an entry for MyPictures.  Great, my code then was:

this.openFileDialog1.InitialDirectory = Environment.SpecialFolder.MyPictures; 

but this didn't compile, and it didn't show up in Intellisense.  I read a few examples online which used the following syntax:

this.openFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures); 

and it worked, although it still didn't show up in Intellisense.  you'd think MS would at least explain this in the docs, weird.

Monday, 24 May 2004 12:34:15 (GMT Daylight Time, UTC+01:00)  #    Comments [1]  .Net Windows Forms