Link to home
Start Free TrialLog in
Avatar of Craig Beamson
Craig BeamsonFlag for United Kingdom of Great Britain and Northern Ireland

asked on

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?
Avatar of Joe Fulginiti
Joe Fulginiti

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.
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
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 ?)
Avatar of Craig Beamson

ASKER

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?
Sorry, I had a lot of tabs open and the link was for another post.  I deleted it.
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

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.
Please post the class myRequest here.

Thanks
 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

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

Open in new window

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.
ASKER CERTIFIED SOLUTION
Avatar of Fernando Soto
Fernando Soto
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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!
Not a problem Beamson, glad I was able to help.