Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RootStorage.ReadOnly() cannot open a file being used by another process #260

Open
conormcg94 opened this issue Dec 1, 2024 · 12 comments
Open

Comments

@conormcg94
Copy link
Contributor

Calling RootStorage.ReadOnly() throws an IO Exception when trying to read a file used in another process.
Whereas V2.4 opened the file for reading as default.

This can be resolved by setting the FileShare parameter to ReadWrite for System.IO.File.Open().

@jeremy-visionaid
Copy link
Collaborator

jeremy-visionaid commented Dec 1, 2024

Perhaps this is more of a documentation/sample thing, basically only two modes are supported (and have unit tests):

  1. Multiple readers (i.e. RootStorage.OpenRead)
  2. Single reader/writer (i.e. RootStorage.Open)

The omission of the FileShare parameter is somewhat deliberate, since SWMR etc. is not supported (AFAIK it also wasn't supported in v2.3/2.4 even if it was allowed by the API). i.e. FileAccess.ReadWrite with anything but FileShare.None should throw until/unless SWMR gets implemented.

@jeremy-visionaid
Copy link
Collaborator

Perhaps this should be closed as a duplicate of #236?

@conormcg94
Copy link
Contributor Author

I have documents that are locked when opened in an application and I'm using openmcdf to read the documents in a separate process.
I don't need SWMR for this as the editing and saving is performed in the main application.
This has been working well with V2.3 but isn't possible with the V3 api. I understand if you don't wont to expose FileShare. Could we add a new method then to cover my use case?

#236 was tagged as an enhancement. I raised this to cover the loss of functionality.

@jeremy-visionaid
Copy link
Collaborator

Right, but what I'm trying to say is that that situation isn't supported, either in 2.3/4 or 3.0.

IMHO this isn't so much a loss of functionality as it is a bug in 2.3/4 that it even allows you to try... The other application might alter the file at any given moment, and both 2.3/4 and 3.0 assume they have exclusive access (2.x even more so than 3) - the only difference is that 3.0 prevents the caller from doing it since it's dangerous.

Can I ask what the other application is, and/or whether you have control of it to do something else?

If you still really really want to do it despite knowing that it isn't supported and might crash etc., then you can always open the file whichever way you want and pass it to one of the stream-based constructors. There's nothing that can be done to prevent it being misused in that way, but it does raise an interesting point... maybe the 3.0 constructors shouldn't actually allow streams to be passed in directly, but only accept something that can enforce locking similar to ILockBytes (an oversight on my part that people wouldn't try and use it in this way).

@jeremy-visionaid
Copy link
Collaborator

jeremy-visionaid commented Dec 2, 2024

As an additional hint, if you just want to quickly open and close a file, then depending on what the other process is doing, then you might be able to do something like:

using (FileStream stream = File.OpenRead(fileName))
{
    stream.Lock(0, stream.Length);
    using (var rootStorage = RootStorage.Open(stream, StorageModeFlags.LeaveOpen))
    {
        // Do stuff
    }
    stream.Unlock(0, stream.Length);
}

Given that the other process hasn't prevented sharing, I think that it's likely to work OK (on Windows at least - locking on other operating systems is generally advisory only)

@jeremy-visionaid
Copy link
Collaborator

Also, thanks very much for reporting this - although the response might not have been what you wanted to hear, it is great and valuable feedback, and I'll have a think about if/how the API should be changed accordingly.

@conormcg94
Copy link
Contributor Author

I'm working on add-ins for CAD programs so I have full control of the file reading/writing but there are times when I want to read the file properties directly. Thank you for your help. I'll pass the filestream into the constructor and am happy to close this issue.

@jeremy-visionaid
Copy link
Collaborator

Oh, it's OK to keep the issue open for now. There are things we might be able to do or change... But I think if we're opening a file that is also open for write in another process then we should probably also lock the stream. Without implementing SWMR properly, this likely equates to locking the whole file, even just for reading it. So, we could consider this ticket to be the "multiple reader" part of SWMR (while some other implementation does the writing), and that could be done independently of the "single writer" part.

@jeremy-visionaid
Copy link
Collaborator

@conormcg94 How did you get on with this one? Did the CAD application function OK with the file locks?

@conormcg94
Copy link
Contributor Author

Yes the file locks work although I still have to open the filestream with FileShare.ReadWrite.
The CAD document also provides an IStorage interface for reading/writing to the file. I have my own basic implementation for interacting with this. Would it be possible to publish the StructuredStorage project for this purpose?

@jeremy-visionaid
Copy link
Collaborator

The structured storage project doesn't really do very much - it's just some wrappers around P/Invokes from CsWin32 - you're welcome to copy it and do what you want with it. It's Windows specific, so not really in line with the goals of this project exactly. If you're OK with it being Windows specific then you could just use the Win32/COM API without really needing OpenMcdf. It raises an interesting point though, whether the StructuredStorage reference has value as a NuGet package in itself...

I'm curious about what you mean by the CAD document providing an IStorage interface?

@conormcg94
Copy link
Contributor Author

The cad application has a method for opening sub-storage within the CAD document, it returns a pointer to an object which implements win32 IStorage. The structured storage project is useful for creating an System.IO.Stream wrapper to pass into openmcdf RootStorage

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants