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

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?
LVL 1
BeamsonAsked:
Who is Participating?
 
Fernando SotoRetiredCommented:
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

0
 
Joe FulginitiNetwork EngineerCommented:
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.
0
 
Joe FulginitiNetwork EngineerCommented:
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
0
Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
Fabrice LambertFabrice LambertCommented:
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 ?)
0
 
BeamsonAuthor 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?
0
 
Joe FulginitiNetwork EngineerCommented:
Sorry, I had a lot of tabs open and the link was for another post.  I deleted it.
0
 
Fernando SotoRetiredCommented:
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

0
 
BeamsonAuthor 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.
0
 
Fernando SotoRetiredCommented:
Please post the class myRequest here.

Thanks
0
 
BeamsonAuthor 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

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

Open in new window

0
 
BeamsonAuthor 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.
0
 
BeamsonAuthor 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!
0
 
Fernando SotoRetiredCommented:
Not a problem Beamson, glad I was able to help.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.