This project is read-only.

Differences across versions

Jan 5, 2009 at 8:32 PM
Edited Jan 5, 2009 at 8:34 PM
Hi Jeff, thanks for the nifty little library.

I've inherited a project which makes use of what I take to be an older version of the RAS stuff project. It is called something like RAS Library for .NET 1.1 and has your name on it, so I hope it is indeed related. I'm posting to ask a few questions:

1) As the developer who worked with the old version does not recall or have any documentation concerning what he did, I'm on my own when adding some functionality to our application. Hence: what are the differences between the version I have in the old code and the DotRas currently available? One thing I have seen is that at least your sample does not recognize when it cannot connect to a VPN-style connection. Either that or I am getting some sort of other confound, hence:
2) I am wanting to use DotRas to open a VPN connection on demand programmatically and close it when done. The sample code does not recognize that the connection has failed: I set up a dummy entry using the network wizard in WinXP just to see how it works. I can use this with the app mentioned above and it tries to connect and fails, as it should, when the VPN address is 127.0.0.1. Whereas with the new library and its demo app it simply returns to the console app with the networking control panel reporting "connecting" which requires the restart your information warns about.
So,
3) Is there any way to force Windows to disconnect an opening interface? Having to restart my workstation everytime I run through a debugging session to figure this and other problems (I am sure I will run into some as I develop my stuff!) is annoying, to say the least. Moreover:
4) I notice when the "port" is stuck open like that the demo application crashes with an invalid handle. Do you have any plans for more error handling, and better yet, documentation for the project?
Jan 6, 2009 at 2:52 PM
Hello!

"what are the differences between the version I have in the old code and the DotRas currently available?"

Basically the new version hosted here is a complete rewrite of the original code since I didn't have it available, and the old code was a mess simply put. I was having a lot of issues with that assembly that I couldn't take care of simply because of how it was written, making maintenance of that assembly nearly impossible.

The PhoneBook class no longer exists, and I've simply exposed all the implemented APIs directly through the RasHelper static class.

"One thing I have seen is that at least your sample does not recognize when it cannot connect to a VPN-style connection."

Well, the new assembly is designed to asynchronously call the dialing mechanism rather than the mess I tried to do in the original assembly. All you need to do now is provide the Dial method with a RasDialCallback to receive messages with what's happening in the dialer.

"Is there any way to force Windows to disconnect an opening interface?"

There sure is! As stated above, you can tell when the dialer fails inside the callback method provided to the RasHelper.Dial method and disconnect it from in there. I noticed the same issue when I kept repeatedly trying to connect and disconnect entries. If you provide the RasDialCallback and check the error messages that are provided by the dialer, you can call the RasHelper.HangUp method using the connection handle also passed into the callback method.

If you need some sample code to look at to accomplish this let me know, I could always create another wiki page demonstrating how to accomplish this.

"Do you have any plans for more error handling, and better yet, documentation for the project?"

Well, as for error handling probably not. I'm leaving that up to the developers using the assembly to handle what they want to do with the errors that occur while dialing entries. In the early development stages I was debating about building the PhoneBook class again like the old assembly had used, but that became such a shoddy way to interface with the dialer it made more of a mess than it helped. Besides, with the callers being able to handle it themselves you'll have a lot more control over what's going on when errors do occur.

I was looking at getting NDoc again to run on the solution since the entire project is fully intellisensed so I'd have the nice html or chm documentation to provide people using the assembly, but since I'm still in the stage of release I was going to wait a bit more before I did that. Don't get me wrong, this assembly is more stable in CTP form than that old cludge of a mess I wrote years ago.

I've been waiting for feedback from people using the old assembly on what they'd like to see me do with it before I start making any major modifications to this one.
Jan 6, 2009 at 3:16 PM
Thanks for the reply; I have some follow-up remarks.

Pertaining to the second and third points, sample code would be appreciated, yes. I'll see what I can put together myself without it, of cousre.

As for errors, then, I take it that the RAS stuff in the Win32 API itself will just pass its errors up to the assembly, so I sniff for those? Sort of what I had in mind was a bunch of predeveloped exception objects but I guess I'll look into building those myself.

Documentation: Anything you produce will be welcome.

I wonder, however, if you will get much feedback on either this or the old one, since I had a heck of a time trying to chase down you and the current version at all: lots of stale links and very little discussion. Mind you, as I said I had no documentation or anything from the previous developer. I find this odd: dialup may be dead (at least amongst a ceratin mindset) but VPN is all the rage, and having one available dynamically seems to me to be a useful goal in many applications. Oh well.
Jan 6, 2009 at 3:40 PM
Edited Jan 6, 2009 at 11:29 PM
Well, if you want your application to throw exceptions you can always use the Win32Exception class that's in .NET. It should be able to translate the error code returned by the callback into a human readable message for you. The exceptions that the assembly throws are for compliance with the logic that I read in the MSDN library for the API that was implemented.

I'll try and get an example worked up and stuck on the Getting Started page sometime today if possible. Been working a lot of extra hours lately, so I haven't had as much time to devote to the project as I'd like.

"As for errors, then, I take it that the RAS stuff in the Win32 API itself will just pass its errors up to the assembly, so I sniff for those?"

Yep. There are error codes returned with the callback that contains the id number of the error that occurred. Also, I've already got all of the error codes mapped out in the DotRas.Constants.RasError class so you won't have to guess what the numbers are for. Suppose I could make it a flag enum rather than constants, might make it easier to work with so you're not having to guess which constant the error code ties to.
Jan 6, 2009 at 3:48 PM
Followup: I think I've discovered some of the source of weirdness. Explorer (in the Network Connections window) reports a "status" called "connecting" which does not correspond to any of the states in your class or in the underlying API. This does not get passed back up and so this does not appear in the connections object, etc.
Jan 6, 2009 at 3:53 PM
That's just because Windows doesn't report all of the states that occur while a connection is being established. There's like 8-10 states that happen during the "connecting" phase that Windows displays that the user doesn't need to know about. When you're working directly with the API you see all states notify the callback method since you might need to do something during the handshaking.
Jan 6, 2009 at 4:15 PM
One posssible error condition for my app as I see it so far is just too long a wait for the VPN or its underlying physical layer to be available. (We want to be able to fall back to another interface if this happens.) Consequently, I think I have to be able to catch this condition somehow. (I find it odd that the XP VPN software seems to be trying forever, too.) Is there any way for me to do so? (User intervention is not an option.)
Jan 6, 2009 at 4:46 PM
Unfortunately you caught me on the day where I just purchased Vista the night before. I'm currently reinstalling everything (yay!) so I can't test anything I write in here before I post it, so take it with a grain of salt. I just browsed over the code and it looks like there isn't anything you can pass into the dial API that'll handle timeouts for you, so you'll need to make something yourself.

You'll at least need to store the last connection state that was received from the callback along with the handle of the connection currently being dialed since you will need to disconnect it if the connection attempt does in fact timeout on you.

This might give you an idea where to start: (don't forget this code is going to be non-functioning).

using System;
using System.Timers;

class Dialer {
    private IntPtr _handle;
    private int _state;
    private Timer _timer;

    public Dialer() {
        this._timer = new Timer(this.timer_Elapsed);
    }

    public void Start() {
        // You'll want to setup your connection information prior to starting the timer unless you want that 
        // included in the amount of time required to connect.

        this._timer.AutoReset = true;
        this._timer.Start();

        this._handle = RasHelper.Dial(..., new RasDialCallback(this.rasDial_Callback));
    }

    void rasDial_Callback(int state) {
        this._state = state;
    }

    void timer_Elapsed(object sender, EventArgs e) {
        if (this._state != CONNECTED && this._handle != IntPtr.Zero) {
            RasHelper.HangUp(this._handle);
            this._timer.Stop();
        }
        else if (this._state == CONNECTED) {
            this._timer.Stop();
        }
    }
}
Jan 6, 2009 at 6:41 PM
Thanks for the suggestion, and good luck with your Vista install. However, the above approach does not work. One cannot call hangup on a connecting interface as it has no handle to use (and if one calls hangup on a null handle, one gets an invalid handle exception, not surprisingly). If, however, the VPN interface does eventually report a time out I can make use of it, though I cannot see at the moment how to get it to time out like it should. I have also discovered that if I set the type of the VPN to L2TP (rather than Automatic), an error will be provoked right away (connection refused or the like, which is what I expect while trying dummy connections).
Jan 6, 2009 at 7:01 PM
As soon as you call the dial method on RasHelper it should be returning the handle of the connection that was just created to dial the entry.

public static IntPtr Dial(string phonebook, RasDialParameters parameters) {

That's the signature of the method directly out of the source code. Are you saying when you're dialing your entry you're not getting a valid handle back?
Jan 6, 2009 at 8:11 PM
There's some sort of weird inconsistent bug if I set up the VPN to be used by all users. I get the correct "refused" error (after two other callbacks, too, I might add, so that works after all) in this case and this case only, if I assign it to "me" (who is an admin, etc.) it works. I somehow cannot now get the "connecting" status in Explorer at all, though from time to time if I manually "dial" the connection it complains about the thing being in use. I am confused. I'll see what happens after my meeting with our infrastructure guys tomorrow - it is possible some of this might prove otiose. Thanks again for all your help.
Jan 6, 2009 at 9:52 PM
Well, that could be a few things actually. More than likely the password isn't being stored for all users and you aren't supplying it to the dial method in the parameters object that's being passed in. Which would explain why you're getting a refused connection.

"I somehow cannot now get the "connecting" status in Explorer at all, though from time to time if I manually "dial" the connection it complains about the thing being in use."

If it's showing that it's in use, the connection isn't being terminated properly all the time when the connection fails. By the way, I have Visual Studio installed again so I should be able to get that sample worked up here in the next few hours hopefully. Suppose I should have added that to my sample applications that were included with the download.
Jan 6, 2009 at 11:03 PM
Oh, I did finally get to check the software and it looks like I did include examples how to properly terminate the connection if it fails during the connection process.

C#:
http://www.codeplex.com/DotRas/SourceControl/changeset/view/26347#369548

VB:
http://www.codeplex.com/DotRas/SourceControl/changeset/view/26347#369552

Basically the RasDial_StateChanged method in both examples is the callback that's passed into the Dial method. If at any time the errorCode is not equal to zero (0) an error has occurred while dialing. This is when you should be ensuring the connection is terminated. Also just FYI if you didn't notice already, returning false from the callback method indicates you no longer wish to receive notifications when the dialing state changed.
Jan 9, 2009 at 6:48 AM
Just thought you might be interested to know I've been working on redesigning how other developers interact with the assembly. I've also added synchronous and asynchronous dialing support into the new RasDialer component (that will support dropping on your forms from the VS toolbox). It will basically handle all functions that interact with the dialer along with retrieving the list of active connections. The timeout functionality is already provided by Windows so you'll simply have to call the Dial() method if you want to have it throw an exception when the timeout occurs.

Might be adding the RasPhonebook class in again to handle phonebook management once I get the dialing functionality finished up.

I have a bit of testing to do on the code before I check it in and I still need to get all the attributes on the properties to work with the IDE correctly, but that doesn't take very long normally.