-
Oct 30th, 2017, 08:50 AM
#1
A way to determine environment
Good morning everyone,
I have an odd problem I've been brainstorming over.
I have a general purpose company application, that we want to install on some of the laptops the sales guys use. The problem is, some functions/modules of this application assumes it's running on a machine internal to our network and do things like access the Active Directory, access fileshares, etc.
These laptops are "on the domain", and in a pinch can be connected via VPN, but I'm looking to make it more "mobile friendly" and write a system so it can access various data via a proxy server.
I'm trying to figure out a good way for the app to "know" it's running on a computer that is currently on a network outside one of our buildings so it can do some things a bit differently.
I considered looking for a file on an internal webserver or fileshare, but don't want to have to wait for a lengthy timeout since this check would be done every time the app is launched. It just didn't seem an "elegant" solution to me. Does anyone know if there's a way to check if a computer is currently attached to a specific domain-controlled network, either wired or wirelessly?
Has anyone had to do anything similar? What did you do?
Thanks in advance for any ideas
-
Oct 30th, 2017, 09:00 AM
#2
Re: A way to determine environment
you could maybe try pinging the host -
Code:
string hostname = "<hostname>";
Ping netping = new Ping();
PingResponse response = netping.PingHost(hostname, 4);
if (response != null)
{
ProcessResponse(response);
}
Please Mark your Thread "Resolved", if the query is solved & Rate those who have helped you
-
Oct 30th, 2017, 09:03 AM
#3
Re: A way to determine environment
Try using System.DirectoryServices.ActiveDirectory.Domain.GetComputerDomain()
The code is in c#, but you should get the gist..
c#.net Code:
static void Main(string[] args).
{
try
{
System.DirectoryServices.ActiveDirectory.Domain domain = System.DirectoryServices.ActiveDirectory.Domain.GetComputerDomain();
string domainName = domain.Name;
Console.WriteLine("You are on " + domainName);
}
catch (Exception)
{
Console.WriteLine("You are not on a domain");
}
}
Tested for both being on a domain and not on a domain.
Process control doesn't give you good quality, it gives you consistent quality.
Good quality comes from consistently doing the right things.
Vague general questions have vague general answers. A $100 donation is required for me to help you if you PM me asking for help. Instructions for donating to one of our local charities will be provided.
______________________________ Last edited by kebo : Now. Reason: superfluous typo's
-
Oct 30th, 2017, 09:19 AM
#4
Re: A way to determine environment
@kebo: Would that work if the computer has "been joined to a domain" but "not on a domain controlled network" such as a home network or a hotel's? This is my problem. All these machines are "joined" to a domain. I need to know when they're "not on the internal network".
@NeedSomeAnswers: I thought about that too, to just ping an internal server and see if I get a response. This was the route I was planning to go with if I didn't hear a more elegant solution on here. The only problem I have with it is I have a launcher program as well with an upgrade routine that looks for new versions of the application and if it finds one, informs the user, and download and installs it. Right now, it's grabbing it from a fileshare, which works great if on the internal network, but stinks if outside on the VPN. I thought about having it change to an FTP based routine for this. Maybe I should just convert the whole system to something that FTP based all the time, internal and external, or completely change my upgrade system. Seems like a huge headache though.
-
Oct 30th, 2017, 09:25 AM
#5
Re: A way to determine environment
When I tested I did so on a domain and got the domain back. I also tested here at my home where I'm on a network, but not on a domain and it threw an exception. Either way, if you are looking to see if the machine is connected to a specific domain, I suppose you could test the domain name if the exception isn't raised.
Last edited by kebo; Oct 30th, 2017 at 09:31 AM.
Process control doesn't give you good quality, it gives you consistent quality.
Good quality comes from consistently doing the right things.
Vague general questions have vague general answers. A $100 donation is required for me to help you if you PM me asking for help. Instructions for donating to one of our local charities will be provided.
______________________________ Last edited by kebo : Now. Reason: superfluous typo's
-
Oct 30th, 2017, 10:22 AM
#6
Re: A way to determine environment
How rapidly into the use of the application do you need to know? The point behind the question is that you have more options if you don't need to access some domain specific resource during startup, but only need it later on. If you have a bit of time, you could do the test on a secondary thread, in which case it really wouldn't matter too much how long it took (within reason). Of course, if the startup sequence is different depending on the domain, then a secondary thread buys you nothing.
My usual boring signature: Nothing
-
Oct 30th, 2017, 10:29 AM
#7
Re: A way to determine environment
Probably the best thing to do is to try and access some server that can only be reached within the network.
Keep in mind auto-detection always needs a safety hatch: suppose a user has an unplugged network cable or wifi was off. It's always friendly to have somewhere in your Options that lets the user toggle the switch back on if, for some reason, auto-detect has a bug.
You don't have to wait a long time for things to time out. Most network APIs have support for specifying timeouts. If they don't, there's lots of ways to implement it yourself.
For example, Ping.Send() has an overload that accepts a timeout value. Odds are if it takes longer than 2s to ping a "local" server, it's not reachable. You may find a 1s timeout fits even better. That's your judgement call. If you use this overload and the timeout is exceeded, the PingReply you get will have the Status property set to IPStatus.TimedOut.
Now let's say you go a little less elegant route and want to look for some file/folder on the network. File.Exists() might work. But there's no asynchronous version, and that means it doesn't support a timeout. I know firsthand network file calls can have LONG timeouts. But I'd be willing to bet it's like pinging: if it takes much longer than 2s it's probably not succeeding at all.
You can convert any synchronous operation to an asynchronous one quite easily. The Task API is a little clunky, but was designed for this. There's a well-established pattern for supporting a timeout (that is also sort of clunky). The idea is you start two different tasks: one 'does the thing' and the other 'is the timeout'. Then you wait on both of them. If the 'does the thing' task finishes first, you didn't time out. If the 'timeout' task finishes first, you timed out:
Code:
Imports System.IO
Public Class Form1
Private Async Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
' ... Display a splash screen or something.
Dim isOnNetwork As Boolean = False
Try
Dim fileExists = Await DetectFileWithTimeoutAsync(TimeSpan.FromSeconds(2))
isOnNetwork = fileExists
Catch ex As Exception
isOnNetwork = False
End Try
' The rest of your load code goes here, it will execute on the UI thread.
End Sub
Public Function DetectFileWithTimeoutAsync(ByVal timeout As TimeSpan) As Task(Of Boolean)
Return Task.Run(Function() DetectFileWithTimeout(timeout))
End Function
Private Function DetectFileWithTimeout(ByVal timeout As TimeSpan) As Boolean
Dim filePath As String = "\\whatever\file"
Dim findFileTask = Task.Run(Of Boolean)(Function()
Return File.Exists(filePath)
End Function)
Dim timeoutTask = Task.Delay(timeout)
Dim finishedIndex = Task.WaitAny(findFileTask, timeoutTask)
If finishedIndex = 0 Then
If findFileTask.Exception IsNot Nothing Then
' Probably log this!
Return False
Else
Return findFileTask.Result
End If
Else
Throw New TimeoutException("Did not find the file in time.")
End If
End Function
End Class
This is a lot less clunky with the Reactive Extensions, for that you need to import some NuGet packages and learn a bit about Reactive programming though :/
Code:
Imports System.IO
Imports System.Reactive.Concurrency
Imports System.Reactive.Disposables
Imports System.Reactive.Linq
Public Class Form1
Private _isOnNetwork As Boolean
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
' ... Display a splash screen or something.
DetectFileAsync().Timeout(TimeSpan.FromSeconds(2)) _
.ObserveOn(CurrentThreadScheduler.Instance) _
.Subscribe(
Sub(success) FinishLoading(success),
Sub(ex) FinishLoading(False)
)
End Sub
Private Sub FinishLoading(ByVal onNetwork As Boolean)
' This will be called on the UI thread, finish loading here.
End Sub
Public Function DetectFileAsync() As IObservable(Of Boolean)
Return Observable.Create(Of Boolean)(Function(o)
Try
Dim fileExists = DetectFile()
o.OnNext(fileExists)
Catch ex As Exception
o.OnError(ex)
End Try
o.OnCompleted()
Return Disposable.Empty
End Function)
End Function
Private Function DetectFile() As Boolean
Dim filePath As String = "\\whatever\file"
Dim exists As Boolean = File.Exists(filePath)
Return exists
End Function
End Class
Both of these assume you need to know "right away", with respect to Shaggy Hiker's question. If you need to know "later", both can be adapted to raise an event or call some particular code when the answer becomes known.
This answer is wrong. You should be using TableAdapter and Dictionaries instead.
-
Oct 30th, 2017, 10:40 AM
#8
Re: A way to determine environment
Yeah, that's the point behind my question: You have lots more options if you have the luxury of at least several seconds of time during startup before you need to know the answer to the question. I've done a few things kind of like this, but I always had the luxury of time.
My usual boring signature: Nothing
-
Oct 30th, 2017, 01:53 PM
#9
Re: A way to determine environment
After reading this question a couple times, I realized what you're really asking for is a way to determine the fastest route and you should treat it as such. As someone who has a lot of experience in managing Windows networks, I can tell you that trying to determine if a particular machine is on a local network, domain or neither is not as clear cut as you might assume. Why ? It's because fundamentally, there is no such thing as a domain or network share. Nearly every network you're dealing with in your daily life is built on the TCP/IP protocol and this protocol is what all data transfers boil down to. TCP/IP only cares about end points. It's to get data from point A to point B. Domains, shared drives, workgroups etc are only abstractions meant to make the whole thing more manageable for humans. Even if you find a way to determine if a machine is connected to a domain controlled network it's never going to be 100% effective. A simple thing like an unplugged network cable or an improperly configured DHCP can lead to a lot of false positive and false negative results in your algorithms.
Instead, I want you to completely abandon this idea of trying to determine what kind of network you're on. While there are times where it might be important, this is not one of them. The beating heart of what you really want to do is access remote data as quickly as possible. Look at the problem like that. If you have multiple ways to access the data based on if the machine in question is locally connected or outside, you should test once using both methods. If one fails and one succeeds, use the one that succeeds. If both methods succeed, use the one that gets the data faster. If both fail, inform the user that the data is inaccessible via an error message.
If I were writing this application, I'd simply use Winsock. By using Winsock, you can get past all the messy abstractions like Domains and work groups and deal with this problem in it's purest form. All I want to know if if I can connect from machine A to machine B. Winsock doesn't make a distiction between remote machines and local network machines. Every machine is a remote machine as far as it's concerned so right there you avoid a lot of problems. You get a very easy one size fits all solution that is fast and elegant. The problem of where the local machine is can very easily be solved. If you can connect to your Server using a private IP address, then you're 100% certain you're on a local network, if you can only connect via a public IP, then you know for certain you're not on the local network. No need to check or domains and all that mess.
Now you don't have to use Winsock. You can use abstractions like shared drives as long as you keep your concerns limited to whether you can reach the remote or not and how fast you can send/receive data.
Last edited by Niya; Oct 30th, 2017 at 02:09 PM.
-
Oct 31st, 2017, 03:57 AM
#10
Re: A way to determine environment
Your other option is to do your upgrade over a web service.
If you have a hosted web service, it would be available the same internally and externally, you can use web service to download files and in fact its a common approach applications use to upgrade.
The downside with this is you will need to host your web service somewhere which will likely have a cost associated with it, unless you have MSDN which comes with free Azure credits, and then you could spin up an Azure web server.
Please Mark your Thread "Resolved", if the query is solved & Rate those who have helped you
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|