[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

VB.NET Service unable to Write file to UNC location

Posted on 2006-05-24
26
Medium Priority
?
965 Views
Last Modified: 2012-05-05
Hello all at EE

I have a Class within a VB.NET service that needs to create a WAV file and write some header info into it onto a network share. I also need to have a form to interact with the service. I get the error, "Access denied" when writing the WAV file. This is because the service is running as local system and does not have access to the network share.

I havea dedicated network user that I can use to access the share but how do I do that in code, The class function is below.
A quick response to this would be fantastic I have a very tight timscale (Who doesnt) to finish this project.

Many thanks

  Function processWavFile(ByRef strWavFile As Scripting.File, ByRef strSource As String, ByRef strDestination As String) As Boolean
        On Error GoTo err_Renamed
        Dim sTmp As Object
        Dim tmpRecord As clsRecord
        Dim ts As Scripting.TextStream
        'read text file to get info and read it into a new record
        tmpRecord = New clsRecord

strDestination = "\\server\networkshare"

        tmpRecord.AUDIODESTINATION = strDestination & "\" & UserID & "_" & strWavFile.Name

    ts = strWavFile.OpenAsTextStream(Scripting.IOMode.ForReading, Scripting.Tristate.TristateFalse)
    Dim sWhole As String
    sWhole = ts.Read(strWavFile.Size)
    tmpRecord.BODY = Mid(sWhole, 9)
    ts.Close()
    ts = Nothing
        ''''write the new wav file based on the record
'ERROR OCCURS HERE
        writeNewWav(tmpRecord)

        processWavFile = True
        Exit Function
err_Renamed:
Err.Raise(8002, , "error in WAV creation." & Err.Description)
        processWavFile = False
    End Function
0
Comment
Question by:Zorac_da_mantis
  • 12
  • 8
  • 4
  • +1
26 Comments
 
LVL 4

Expert Comment

by:broadbent
ID: 16749898
I've also been stuck on a similar question, but have had no answer - so I will be very interested in your question
0
 

Author Comment

by:Zorac_da_mantis
ID: 16750204
Hi I have been looking around frantically to find a solution but nothing as yet, all I can find are referances to the Win32 api WNetAddConnection2 but no reliable VB.NET code to actually make the connection. In the link http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wnet/wnet/wnetaddconnection2.asp I found on a similar question in EE it states that you dont need to actually map a drive letter by leaving the lpLocalName attribute as NULL.

Does anyone have any knowledge or know how to implement this in VB.NET??

Many thanks
0
 
LVL 2

Expert Comment

by:anwar ul haque
ID: 16750246
i think you shold try to give permissions to asp.net user.
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:Zorac_da_mantis
ID: 16750274
Hi Thanks for the comment, I am not using ASP.NET and the process is being run as a local system account, this will be a development for a firm wide orgainisation so unfortunately granting each local system on their network rights to the resource is not the answer.
0
 
LVL 4

Expert Comment

by:broadbent
ID: 16751513
Impersonation has come up as a possibility
0
 

Author Comment

by:Zorac_da_mantis
ID: 16751678
What do you mean by Impersonation, broadbent??
0
 
LVL 4

Expert Comment

by:broadbent
ID: 16752004
I've only just started looking at this, but I was about to start looking at
http://www.pscode.com/vb/scripts/ShowCode.asp?txtCodeId=963&lngWId=10
0
 
LVL 2

Expert Comment

by:anwar ul haque
ID: 16758734
this is basically permissions probelem, can you create file manually? ..i mean not from within the service if so then try to give proper permissions to this user i mean write permission to that folder.
0
 

Author Comment

by:Zorac_da_mantis
ID: 16758918
Hi

You are right, the user in this case in the Computers LocalSystem account, which is why I need to log on to the share within my application as a different user,

All I want to know is how to make a connection to the share as a user in code, I would imagine something like thi:

Sub connectToShare( ByVal UserID as string, ByVal Password As String, ByVal Domain As String)
'<<< What code goes Here>>>
End Sub
0
 
LVL 4

Expert Comment

by:broadbent
ID: 16759230
The problem I had was finding the user's password, assuming the app is going to be used by others.
0
 
LVL 2

Expert Comment

by:anwar ul haque
ID: 16759322
I think you better need to provide proper logon information to your service. did you get my point?  service should run under some admin user account that has proper priviliages to the shared folder, if you didn't give proper permissions to shared folder try to give those permissions. hope this will work...
0
 

Author Comment

by:Zorac_da_mantis
ID: 16759394
I have both User name and password but I dont know how to push it through in code, thanks for that imersonation code I tried it but it didnt seem to work, good idea though I have also come actoss the CreateProcessAsUser
0
 

Author Comment

by:Zorac_da_mantis
ID: 16759420
Hi

I cannot provide logon credentials to the service because then I lose the ability to pop up a WIndows Form or have a systray icon. The service then does not interact with the users desktop effecetively.
0
 
LVL 2

Expert Comment

by:anwar ul haque
ID: 16759818
i'd tested this scenerio from widin my existing service,
i had add code to create text file to a shared folder initially i didnt have any permissions. It gave me exception "Access to the path "path...."is denied..."
then I gave the domain user full permissions over this folder and (currently I'm running my computer wid domain user account)  and it gave me no error and file is created successfully.
0
 

Author Comment

by:Zorac_da_mantis
ID: 16759829
Is your Service Logging on as a Domain User?? Right click properties, Log On as??
0
 
LVL 2

Expert Comment

by:anwar ul haque
ID: 16759976
nope, my service is running under local system account.
0
 

Author Comment

by:Zorac_da_mantis
ID: 16760004
That i show I got the error in the first place :(
My Windows Logon account is a domain admin, so I have rights to everywhere on the network.
My Service runs as a Local System account and when it needs to access an Active Directory share (\\server\sharename) I get Access denied. While I can browse the path with no problems
0
 
LVL 2

Expert Comment

by:anwar ul haque
ID: 16760030
hmmm im not using active directory here.
0
 

Author Comment

by:Zorac_da_mantis
ID: 16760042
Do you have any knwledge of how to use the CreateProcessAsUser call?? If I can implement this I can get the service to launch an exe as the top level user that will move the files instead.
0
 
LVL 2

Expert Comment

by:anwar ul haque
ID: 16760171
I've found this code from msdn hope it will help

Const LOGON32_LOGON_INTERACTIVE = 2
Const LOGON32_PROVIDER_DEFAULT = 0
Const CREATE_DEFAULT_ERROR_MODE = &H4000000

Private Type STARTUPINFO

   cb As Long
   lpReserved As Long ' !!! must be Long for Unicode string
   lpDesktop As Long  ' !!! must be Long for Unicode string
   lpTitle As Long    ' !!! must be Long for Unicode string
   dwX As Long
   dwY As Long
   dwXSize As Long
   dwYSize As Long
   dwXCountChars As Long
   dwYCountChars As Long
   dwFillAttribute As Long
   dwFlags As Long
   wShowWindow As Integer
   cbReserved2 As Integer
   lpReserved2 As Long
   hStdInput As Long
   hStdOutput As Long
   hStdError As Long

End Type

Private Type PROCESS_INFORMATION

   hProcess As Long
   hThread As Long
   dwProcessId As Long
   dwThreadId As Long

End Type

Private Declare Function CreateProcessAsUser Lib "advapi32.dll" _
      Alias "CreateProcessAsUserA" _
      (ByVal hToken As Long, _
      ByVal lpApplicationName As Long, _
      ByVal lpCommandLine As String, _
      ByVal lpProcessAttributes As Long, _
      ByVal lpThreadAttributes As Long, _
      ByVal bInheritHandles As Long, _
      ByVal dwCreationFlags As Long, _
      ByVal lpEnvironment As Long, _
      ByVal lpCurrentDirectory As String, _
      lpStartupInfo As STARTUPINFO, _
      lpProcessInformation As PROCESS_INFORMATION) As Long

Private Declare Function LogonUser Lib "advapi32.dll" Alias "LogonUserA" ( _
      ByVal lpszUsername As String, _
      ByVal lpszDomain As String, _
      ByVal lpszPassword As String, _
      ByVal dwLogonType As Long, _
      ByVal dwLogonProvider As Long, _
      phToken As Long) As Long

Private Declare Function CloseHandle Lib "kernel32" ( _
      ByVal hObject As Long) As Long

Private Sub Command1_Click()
      Dim hToken As Long
      Dim ulResult As Long
      Dim startup As STARTUPINFO
      Dim process As PROCESS_INFORMATION
   
      ulResult = LogonUser("ImpersonatedUsed", "ImpersonatedDomain", _
         "ImpersonatedUserPassword", LOGON32_LOGON_INTERACTIVE, _
            LOGON32_PROVIDER_DEFAULT, hToken)

      If ulResult = 0 Then
         MsgBox "Error in LogonUser:  " & Err.LastDllError
         Exit Sub
      End If

      startup.cb = Len(startup)
      'TODO: Replace 'mail.exe' with the name of the program you wish to start
      ulResult = CreateProcessAsUser(hToken, 0&, "mail.exe", 0&, 0&, _
         False, CREATE_DEFAULT_ERROR_MODE, 0&, "path", startup, process)

      If ulResult = 0 Then
         MsgBox "Error in CreateProcessAsUser:  " & Err.LastDllError
         Exit Sub
      End If
   
      CloseHandle hToken
   
End Sub

0
 

Author Comment

by:Zorac_da_mantis
ID: 16760257
Cant use this code in .NET :( its VB6 code im using Visual Studio 2003
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 16760392
Here is a Windows impersonator class:

' Source:
'  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemSecurityPrincipalWindowsIdentityClassImpersonateTopic1.asp

Imports System.Runtime.InteropServices
Imports System.Security.Principal
Imports System.Security.Permissions

Public Class WindowsImpersonator

  Private Declare Auto Function LogonUser Lib "advapi32.dll" (ByVal lpszUsername As [String], _
   ByVal lpszDomain As [String], ByVal lpszPassword As [String], _
   ByVal dwLogonType As Integer, ByVal dwLogonProvider As Integer, _
   ByRef phToken As IntPtr) As Boolean

  <DllImport("kernel32.dll")> _
  Public Shared Function FormatMessage(ByVal dwFlags As Integer, ByRef lpSource As IntPtr, _
   ByVal dwMessageId As Integer, ByVal dwLanguageId As Integer, ByRef lpBuffer As [String], _
   ByVal nSize As Integer, ByRef Arguments As IntPtr) As Integer
  End Function

  Public Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Boolean

  Public Declare Auto Function DuplicateToken Lib "advapi32.dll" (ByVal ExistingTokenHandle As IntPtr, _
    ByVal SECURITY_IMPERSONATION_LEVEL As Integer, _
    ByRef DuplicateTokenHandle As IntPtr) As Boolean

  Private m_impersonatedUser As WindowsImpersonationContext

  'GetErrorMessage formats and returns an error message
  'corresponding to the input errorCode.
  Private Shared Function GetErrorMessage(ByVal errorCode As Integer) As String
    Dim FORMAT_MESSAGE_ALLOCATE_BUFFER As Integer = &H100
    Dim FORMAT_MESSAGE_IGNORE_INSERTS As Integer = &H200
    Dim FORMAT_MESSAGE_FROM_SYSTEM As Integer = &H1000

    Dim messageSize As Integer = 255
    Dim lpMsgBuf As String
    Dim dwFlags As Integer = FORMAT_MESSAGE_ALLOCATE_BUFFER Or FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS

    Dim ptrlpSource As IntPtr = IntPtr.Zero
    Dim prtArguments As IntPtr = IntPtr.Zero

    Dim retVal As Integer = FormatMessage(dwFlags, ptrlpSource, errorCode, 0, lpMsgBuf, _
     messageSize, prtArguments)
    If 0 = retVal Then
      Throw New Exception("Failed to format message for error code " + errorCode.ToString() + ". ")
    End If

    Return lpMsgBuf
  End Function     'GetErrorMessage

  Public Sub Impersonate(ByVal domainName As String, ByVal userName As String, ByVal password As String)

    Const LOGON32_PROVIDER_DEFAULT As Integer = 0
    'This parameter causes LogonUser to create a primary token.
    Const LOGON32_LOGON_INTERACTIVE As Integer = 2
    Const SecurityImpersonation As Integer = 2

    Dim tokenHandle As New IntPtr(0)
    Dim dupeTokenHandle As New IntPtr(0)
    Try


      tokenHandle = IntPtr.Zero
      dupeTokenHandle = IntPtr.Zero

      ' Call LogonUser to obtain a handle to an access token.
      Dim returnValue As Boolean = LogonUser(userName, domainName, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, tokenHandle)

      If Not returnValue Then
        Dim ret As Integer = Marshal.GetLastWin32Error()
        Throw New Exception(String.Format("Error: [{0}] {1}", ret, GetErrorMessage(ret)))
      End If

      Dim retVal As Boolean = DuplicateToken(tokenHandle, SecurityImpersonation, dupeTokenHandle)
      If Not retVal Then
        CloseHandle(tokenHandle)
        Throw New Exception("Exception thrown in trying to duplicate token.")
      End If

      ' TThe token that is passed to the following constructor must
      ' be a primary token in order to use it for impersonation.
      Dim newId As New WindowsIdentity(dupeTokenHandle)
      m_impersonatedUser = newId.Impersonate()

      ' Free the tokens.
      If Not System.IntPtr.op_Equality(tokenHandle, IntPtr.Zero) Then
        CloseHandle(tokenHandle)
      End If

      If Not System.IntPtr.op_Equality(dupeTokenHandle, IntPtr.Zero) Then
        CloseHandle(dupeTokenHandle)
      End If
    Catch ex As Exception
      Console.WriteLine(("Exception occurred. " + ex.Message))
    End Try

  End Sub

  Public Sub Undo()
    m_impersonatedUser.Undo()
  End Sub


  Public ReadOnly Property CurrentName() As String
    Get
      Return WindowsIdentity.GetCurrent.Name
    End Get
  End Property

End Class

Bob
0
 

Author Comment

by:Zorac_da_mantis
ID: 16760844
Thanks a mil for this Bob, Im not sure if im implementing it correctly though, Heres what I have done:


 Function processWavFile(ByRef strWavFile As Scripting.File, ByRef strSource As String, ByRef strDestination As String, ByRef Field1 As String, ByRef Field2 As String, ByRef Field3 As String, ByRef Field4 As String, ByRef Field5 As String, ByRef Field6 As String, ByRef IMPORTACCOUNT As String) As Boolean
        On Error GoTo err_Renamed
        Dim tmpRecord As clsRecord

'============NEW ADDITION==================================
    '<Declared the class>
        Dim ImpersonateUser As WindowsImpersonator
'========================================================

        Dim ts As Scripting.TextStream
        'read text file to get info and read it into a new record
        tmpRecord = New clsRecord

        tmpRecord.AUDIODESTINATION = strDestination & "\" & UserID & "_" & strWavFile.Name
        tmpRecord.IMPORTACCOUNT = UserID
        tmpRecord.FIELD1 = WorkType


    ts = strWavFile.OpenAsTextStream(Scripting.IOMode.ForReading, Scripting.Tristate.TristateFalse)
    Dim sWhole As String
    sWhole = ts.Read(strWavFile.Size)
    tmpRecord.BODY = Mid(sWhole, 9)
    ts.Close()
    ts = Nothing

        ''''write the new wav file based on the record
'==========NEW ADDITION==================================
      '<Called the impersonate Function>
        ImpersonateUser.Impersonate("domainname", "MyUserID", "MyPasword")
'======================================================

        writeNewWav(tmpRecord)
        processWavFile = True
        Exit Function
err_Renamed:
Err.Raise(8002, , "error in WAV creation." & Err.Description)
        processWavFile = False
    End Function


I placed the code you sent me in its own class file and all seems to work but when I call the Impersonate call I get the following error in my Log

Object reference not set to an instance of an object.

Please excuse my inexperiance but where am I going wrong??

0
 
LVL 96

Accepted Solution

by:
Bob Learned earned 2000 total points
ID: 16761014
You need an instance of that class:

Dim ImpersonateUser As New WindowsImpersonator

Bob
0
 

Author Comment

by:Zorac_da_mantis
ID: 16761117
THANK YOU THANK YOU THANK YOU

That works like a charm now.

0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article explains how to create and use a custom WaterMark textbox class.  The custom WaterMark textbox class allows you to set the WaterMark Background Color and WaterMark text at design time.   IMAGE OF WATERMARKS STEPS Create VB …
If you're writing a .NET application to connect to an Access .mdb database and use pre-existing queries that require parameters, you've come to the right place! Let's say the pre-existing query(qryCust) in Access takes a Date as a parameter and l…
When cloud platforms entered the scene, users and companies jumped on board to take advantage of the many benefits, like the ability to work and connect with company information from various locations. What many didn't foresee was the increased risk…
With just a little bit of  SQL and VBA, many doors open to cool things like synchronize a list box to display data relevant to other information on a form.  If you have never written code or looked at an SQL statement before, no problem! ...  give i…

873 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question