0

My goal:

I am trying to access the C Drive of a server on our network, with the goal of searching for a certain file extension and returning the paths of those files. I found code here which works perfectly, but only if I am searching through my local computer, or the IP of a server that I have already been authenticated to; when I try to run the code to the IP of a server which I have not authenticated to it does not work.

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Cursor = Cursors.WaitCursor
    DirSearch("\\10.0.15.87\c$")
    Cursor = Cursors.Default
End Sub

Public Sub DirSearch(ByVal sDir As String)
    Try
        For Each dir As String In Directory.GetDirectories(sDir)
            For Each file In Directory.GetFiles(dir, "myfile.extension")
                MsgBox(file)
            Next
            DirSearch(dir)
        Next
    Catch ex As Exception
        Debug.WriteLine(ex.Message)
    End Try
End Sub

When I run the code:

I believe the code executes to completion; the cursor turns into the WaitCursor for about half a second before returning back to normal, at which point no errors are thrown and everything continues as normal. If I change "10.0.15.87" to "10.0.15.81" (which I have already manually authenticated to) then I get a few MsgBox's pop up with file paths. (I am sure that the issue is not within searching for the file, I know that it works for other ip's and that the files I am searching for are in the c drive of 10.0.15.87 - I believe I just need to pass authentication.)

This is what happens when I attempt to browse to ip 87 manually:

enter image description here

Information about 10.0.15.87:

  • It is not connected to the domain unlike the computer where I am running the code from. (Neither is 10.0.15.81)
  • It has a static ip
  • If I manually try to access it I can enter the credentials as per the image above, I just need to pass the credentials through the code.

How do I go about passing authentication before attempting to access the server?

Tyler N
  • 301
  • 2
  • 14

1 Answers1

1

Update: I believe I have found the simplest and most consistent method, methods 2 & 3 may or may not work consistently I've found.

Method 1 - Calling Net Use (Works consistently and across domains)

The Net Use Command allows you to store credentials to shares through the command prompt and you are able to call this from .NET very easily.

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    Dim proc As New Process
    proc.StartInfo.FileName = "net"
    proc.StartInfo.UseShellExecute = True
    proc.EnableRaisingEvents = False

    Dim server As String = "Server"
    Dim user As String = "User"
    Dim pass As String = "Pass"

    If EnsureConnection(server) Then
        proc.StartInfo.Arguments = "use \\" & server & "\IPC$ /u:" & server & "\" & user & " " & pass
        proc.Start()
    End If
End Sub

Function EnsureConnection(server As String)
    'Give more or less ping attempts depending on how reliable your connection is.
    'I have found that one ping can give false negative easily even on reliable connections
    If My.Computer.Network.Ping(server) Then
        Return True
    ElseIf My.Computer.Network.Ping(server) Then
        Return True
    Else
        Return False
    End If
End Function

This method will store the credentials on the machine until the machine shuts down or reboots and the user will be authenticated outside of the program as well. If that is not desirable you could always use the '/delete' switch of the command to delete it as soon as the program is done with it.

Method 2 - Using functions in MPR.DLL

I eventually found this answer to another question. This is the method that worked for me. This is what it looks like integrated into my project:

Imports System.IO    
Imports System.Runtime.InteropServices

Public Class Form1 
    Dim user As String
    Dim pass As String
    Dim path As String

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Cursor = Cursors.WaitCursor
        user = "UsernameString"
        pass = "PasswordString"
        path = "\\10.0.15.87\c$"
        MPRAuth()
        DirSearch(path)
        Cursor = Cursors.Default
    End Sub

    Private Sub MPRAuth()
        Dim nr As New NETRESOURCE
        nr.dwType = RESOURCETYPE_DISK
        nr.lpRemoteName = path
        If WNetAddConnection2(nr, pass, user, 0) <> NO_ERROR Then
            Throw New Exception("WNetAddConnection2 failed.")
        End If
        If WNetCancelConnection2(path, 0, True) <> NO_ERROR Then
            Throw New Exception("WNetCancelConnection2 failed.")
        End If
    End Sub

    Public Sub DirSearch(ByVal sDir As String)
        Try
            For Each dir As String In Directory.GetDirectories(sDir)
                Try
                    For Each file In Directory.GetFiles(dir, "*.exe")
                        MsgBox(file)
                    Next
                Catch ex As Exception
                    Debug.WriteLine(ex.Message)
                End Try
                DirSearch(dir)
            Next
        Catch ex As Exception
            Debug.WriteLine(ex.Message)
        End Try
    End Sub

    <StructLayout(LayoutKind.Sequential)>
    Private Structure NETRESOURCE
        Public dwScope As UInteger
        Public dwType As UInteger
        Public dwDisplayType As UInteger
        Public dwUsage As UInteger
        <MarshalAs(UnmanagedType.LPTStr)>
        Public lpLocalName As String
        <MarshalAs(UnmanagedType.LPTStr)>
        Public lpRemoteName As String
        <MarshalAs(UnmanagedType.LPTStr)>
        Public lpComment As String
        <MarshalAs(UnmanagedType.LPTStr)>
        Public lpProvider As String
    End Structure

    Private Const NO_ERROR As UInteger = 0
    Private Const RESOURCETYPE_DISK As UInteger = 1

    <DllImport("mpr.dll", CharSet:=CharSet.Auto)>
    Private Shared Function WNetAddConnection2(ByRef lpNetResource As NETRESOURCE, <[In](), MarshalAs(UnmanagedType.LPTStr)> ByVal lpPassword As String, <[In](), MarshalAs(UnmanagedType.LPTStr)> ByVal lpUserName As String, ByVal dwFlags As UInteger) As UInteger
    End Function

    <DllImport("mpr.dll", CharSet:=CharSet.Auto)>
    Private Shared Function WNetCancelConnection2(<[In](), MarshalAs(UnmanagedType.LPTStr)> ByVal lpName As String, ByVal dwFlags As UInteger, <MarshalAs(UnmanagedType.Bool)> ByVal fForce As Boolean) As UInteger
    End Function
End Class

Method 3 - NetworkCredential and CredentialCache

This method converts the ip to a URI and caches the credentials. I was unable to make this method work with a server on a different domain than me.

Imports System.Net

Dim builder As New UriBuilder("10.0.15.87")
Dim uri As Uri = builder.Uri
Dim netCred = New NetworkCredential("UsernameString", "PasswordString", "DomainString")
Dim netCache = New CredentialCache()
netCache.Add(uri, "Basic", netCred)
DirSearch("\\10.0.15.87\c$")

Method 2 is a lot less involved than Method 2 and it worked fine for me so long as the server I was attempting to connect to was on the same domain as me. When I tried to connect to server not on the domain I got the error 'User name or password is incorrect' which is not true, so I'm not sure where the issue was - perhaps someone else knows. Method 2 has worked flawless for me so far for any server no matter the domain or if have manually authenticated to it before running the code. Hope this helps others!

Tyler N
  • 301
  • 2
  • 14