VB - ensuring a text file is written to and closed before opening to read from it

Beamson
Beamson used Ask the Experts™
on
I'd like to read the report or text file generated below (3rd from last line) into a textbox:
        Dim config As New MarketplaceWebServiceConfig
        config.ServiceURL = "https://mws.amazonservices.co.uk"
        Dim myClient As New MarketplaceWebServiceClient(AccessKey, SecretKey, ApplicationName, ApplicationVersion, config)
        Dim myRequest As New GetReportRequest
        myRequest.Merchant = SellerID
        myRequest.ReportId = reportId
        myRequest.Report = File.Open("C:\\report\" + reportId.ToString + ".txt", FileMode.OpenOrCreate, FileAccess.ReadWrite)
        Dim myResponse As New GetReportResponse
        myResponse = myClient.GetReport(myRequest)

Open in new window


If I add a final line:
myTextbox.text = File.ReadAllText("C:\\report\" + reportId.ToString + ".txt")

Open in new window

I get "The process cannot access the file "xxx" because it is being used by another process."

What do I need to do to ensure the file is written to and closed before attempting to read from it?
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Joe FulginitiNetwork Engineer

Commented:
For starters, you should place your code in a try statement:

try
       Dim config As New MarketplaceWebServiceConfig
        config.ServiceURL = "https://mws.amazonservices.co.uk"
        Dim myClient As New MarketplaceWebServiceClient(AccessKey, SecretKey, ApplicationName, ApplicationVersion, config)
        Dim myRequest As New GetReportRequest
        myRequest.Merchant = SellerID
        myRequest.ReportId = reportId
        myRequest.Report = File.Open("C:\\report\" + reportId.ToString + ".txt", FileMode.OpenOrCreate, FileAccess.ReadWrite)
        Dim myResponse As New GetReportResponse
        myResponse = myClient.GetReport(myRequest)
catch ex as exception
        response.write("Failed to access file and got the following error: " + ex.message)
        'or some other type of code to handle the error.
end try

Open in new window


Since it looks like this is a web app, you should be writing this to the database and then perhaps write another program that pulls the information from the database and writes it to a file.  The program can be a console program that gets called from the task scheduler.  This will prevent more than one program from accessing files at the same time.
Joe FulginitiNetwork Engineer

Commented:
Something like this is what I have been looking at for a similar project.  Reviews are good and you can just plug it into the router.

https://www.amazon.com/NETGEAR-LTE-Modem-Broadband-Connection/dp/B01N5ASNTE/ref=sr_1_2?ie=UTF8&qid=1510678769&sr=8-2&keywords=cellular+router
Fabrice LambertConsulting
Distinguished Expert 2017

Commented:
Hi,

In your code, make sur that any opened file is properly closed, even if errors happen (check error handlers).

But, if the file is opened by an external process, there isn't much you can do, beside handling and reporting the error, so the user can try again.

Somes might say that you can try to read it again after a little amount of time, but that's running the risk of locking your application forever (what if the file is locked bc of a crash ? Or was never properly closed ?)
Rowby Goren Makes an Impact on Screen and Online

Learn about longtime user Rowby Goren and his great contributions to the site. We explore his method for posing questions that are likely to yield a solution, and take a look at how his career transformed from a Hollywood writer to a website entrepreneur.

Author

Commented:
Okay, some mixed suggestions there.

Joe: that's a fair point - I've omitted the Try Catch in my sample code for simplicity (I have it in my main code).
However, this part of the code works fine, without error.
It is only the addition of an attempt to read back from the file immediately afterwards that fails.
Your second comment just seemed to be a link to buy a router!

Fabrice: there should be no external process which accesses the file.  I agree any open file should be closed.  My question is about how I wait for the action of writing to the file to end before closing the file and attempting to read the file back.
I could do a wait/retry loop but is there some other way of waiting for the previous task (writing to the file) to complete before moving on to the next file open / read commands?
Joe FulginitiNetwork Engineer

Commented:
Sorry, I had a lot of tabs open and the link was for another post.  I deleted it.
Fernando SotoRetired
Distinguished Expert 2017

Commented:
Hi Beamson;

This line of code opens the file for read/write and assigns the return a FileStream to myRequest.Report at which point you relinquish control of the open file to the object myRequest. In that object it should close the file when it is finished using it so that calls to read it from other parts of your app does not fail.
myRequest.Report = File.Open("C:\\report\" + reportId.ToString + ".txt", FileMode.OpenOrCreate, FileAccess.ReadWrite)

Open in new window

I assume that after this line of code is executed the myRequest object has completed using the file and should have closed it.
 myResponse = myClient.GetReport(myRequest)

Open in new window

Author

Commented:
should have closed it

I agree, the file should be closed but for some reason it is not.
If I debug the sub with a long system.threading value (few minutes) and then try to access the textfile I get the same problem.

If I debug it with no system.threading value, let is fail, then open the same file from another subroutine, it opens fine.  It is as though the process that creates and writes to the file is not releasing the file once written to.

If I separate the writing and reading back of the text file into two separate subs and run them in the same debugging session, they also fail with the same error when reading back.  I feel as though I need to explicitly release the file somewhere but don't know where or how.
Fernando SotoRetired
Distinguished Expert 2017

Commented:
Please post the class myRequest here.

Thanks

Author

Commented:
 Dim myRequest As New GetReportRequest

Open in new window


myRequest.Merchant = SellerID
        myRequest.ReportId = reportId
        myRequest.Report = File.Open(MyPath+ reportId.ToString + ".txt", FileMode.OpenOrCreate, FileAccess.ReadWrite)

Open in new window


Imports System
Imports System.IO
Imports System.Xml.Serialization
Imports MarketplaceWebService.Attributes

Namespace MarketplaceWebService.Model
    <MarketplaceWebService(RequestType:=RequestType.DEFAULT, ResponseType:=ResponseType.STREAMING)> <XmlRoot([Namespace]:="http://mws.amazonaws.com/doc/2009-01-01/", IsNullable:=False)> <XmlType([Namespace]:="http://mws.amazonaws.com/doc/2009-01-01/")>
    Public Class GetReportRequest
        Public Sub New()

        <Obsolete("Not used anymore. MWS ignores this parameter, but it is left in here for backwards compatibility.")> <XmlElement(ElementName:="Marketplace")>
        Public Property Marketplace As String
        <XmlElement(ElementName:="Merchant")>
        Public Property Merchant As String
        <XmlElement(ElementName:="MWSAuthToken")>
        Public Property MWSAuthToken As String
        <MarketplaceWebServiceStream(StreamType:=StreamType.RECEIVE_STREAM)>
        Public Property Report As Stream
        <XmlElement(ElementName:="ReportId")>
        Public Property ReportId As String

        <Obsolete("Not used anymore. MWS ignores this parameter, but it is left in here for backwards compatibility.")>
        Public Function IsSetMarketplace() As Boolean
        Public Function IsSetMerchant() As Boolean
        Public Function IsSetMWSAuthToken() As Boolean
        Public Function IsSetReport() As Boolean
        Public Function IsSetReportId() As Boolean
        <Obsolete("Not used anymore. MWS ignores this parameter, but it is left in here for backwards compatibility.")>
        Public Function WithMarketplace(marketplace As String) As GetReportRequest
        Public Function WithMerchant(merchant As String) As GetReportRequest
        Public Function WithMWSAuthToken(mwsAuthToken As String) As GetReportRequest
        Public Function WithReport(report As Stream) As GetReportRequest
        Public Function WithReportId(reportId As String) As GetReportRequest
    End Class
End Namespace

Open in new window

Fernando SotoRetired
Distinguished Expert 2017

Commented:
Where is the implementation for this?
Public Function WithReport(report As Stream) As GetReportRequest

Open in new window

Author

Commented:
Not sure how to answer that question.  The classes are provided as part of one of Amazon's dlls.  Will take a look tomorrow and see what I can find when I'm at my desk.
Retired
Distinguished Expert 2017
Commented:
Hi Beamson;

It seems that Amazon is not closing the file so try the following code modification after the line shown.
myResponse = myClient.GetReport(myRequest)
'' Add these two lines after the above line
myRequest.Report.Close()
myRequest.Report.Dispose()

Open in new window

Author

Commented:
That did the trick!

I'd tried variations of that but was focusing on closing the response objects rather than the request ones.
Your way worked immediately.

Thanks for all of your help!
Fernando SotoRetired
Distinguished Expert 2017

Commented:
Not a problem Beamson, glad I was able to help.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial