clientdataset.saveToStream problems

I use clientdataset.LoadFromStream to lock out other users from the file / table.

Is there a way to do the simpler, LoadFromFile, and lock out other users, by setting a parameter, or similar?
GrahamDLovellAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Sinisa VukSoftware architectCommented:
You want to lock table/row(s) in database where you connected?
GrahamDLovellAuthor Commented:
Yes. I cannot just lock rows, since I am using the client data set as my "database", so I have to load the whole table, and then save it completely.

To lock the file, I use TStream, which gives you the option to lock the file in various modes.

I would be happy with this method if SaveToStream worked, but it didn't seem to do so.

I have tried various methods, and my final workaround was to open as a Stream, and then save as file (after freeing the stream), but this seems to be a bit outside of the documentation.

If I could avoid TSteam altogether (if there is an appropriate option in the LoadFromFile method), I would be happy. I would also be happy if I could get TStream to work as expected.
Kyle FosterCEOCommented:
Instead of using the VCL TClientDataset, there is another alternative.  I use TKBMemTable in my product line and have never had any issues with it.

http://www.components4programmers.com/products/kbmmemtable/
Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

GrahamDLovellAuthor Commented:
Thanks for your suggestion, but I would rather resolve my issue with the native code than move to a 3rd party product.
Geert GOracle dbaCommented:
what database type is this ?

locking a whole table is not required in most cases

If you use clientdataset on a file ...
i'm not sure that is multi-user safe.

probably not a good idea to work like that in a multi-user environment
GrahamDLovellAuthor Commented:
The database is a Tclientdataset, saved as a file.

Yes, I agree it is not multi-user safe. However, it is safe in this situation, as I only want one user to use the file. I just want to make sure he (or she) cannot open it again while that user has it open elsewhere.
GrahamDLovellAuthor Commented:
I have discovered my problem relates to a long-standing (not fixed) bug in TClientDataSet.SaveToStream.

The method calls WriteDataPacket(TStream,false,Format). It should call WriteDataPacket(TStream,true,Format), since "false" stops it writing to the stream.

Nils van der Mee has a workaround at http://www.delphigroups.info/2/67/461553.html, which involves creating a descendant procedure that sends the correct WriteDataPacket command. However, I am not sure how to implement this so that I can simply call it for all my existing cds tables.

Can I write a procedure that extends TClientDataSet, so that it just operates as an additional procedure for that class?
GrahamDLovellAuthor Commented:
I have found that it is not hard to add a class helper to do this, but I don't think that Nils' post represents a solution to my problem. I have tried it, and it corrupts my data.

Back to the drawing board.
Geert GOracle dbaCommented:
a file based database has one very big problem in a multi-user (multiple use) environment
> severed network connections

you need a system which keeps track of who is connected
and allows use of resources when they are free.

this comes as part of any rdbms system

clientdataset simply doesn't have this
GrahamDLovellAuthor Commented:
Geert

Thank you for your interest in my problem.

My application is definitely single-user, and I won't be going to a rmbs. I don't need the complication of requiring this to be set and managed, when it is NOT needed for my application. Why should I? It was bad enough with the BDE, when you had to cope with existing installations, etc., and the support complications associated with a tiny software fee. The TClientDataSet was a break through for me in installation and running simplicity.

I have tried every combination of things that I can think of to get SaveToStream to work, but it won't work.

I have a workaround - I will use that, since either no-one has had the same problem, or they have given up, as I am about to do.
Geert GOracle dbaCommented:
in your original question statement you noted this:
to lock out other users

this means you are in a multi-user environment.

you could go the old (dinosaur) way (like paradox used to do)
add a secondary file: file.lck
if the file exists, then someone else is accessing the data

keep the file open with open exclusive deny everything to any else
since you would manage this file yourself you would have total control over it.

if it's impossible to tackle a specific problem, a workaround can be a solution.
GrahamDLovellAuthor Commented:
My original question was imprecise. I want to lock out a single user from opening another application and damaging the data. The design should not allow "another user" access (but it could be done by a dummy with Administrator rights). I was more concerned about the program being instantiated twice (even with a different program name) by the same user.

Will you believe me now? Single User - not multiple-user.

The file.lck was an administrative problem under BDE in the event of a crash, since it was left open. TStream is easier to administer.

It looks like a work-around is required.
Sinisa VukSoftware architectCommented:
I use this code for single instance - put in .dpr source file
..
var
  Mutex : THandle;
  bAppRunning: Boolean;

begin
  Mutex := CreateMutex(nil, True, 'UNIQUE_MUTEX_STRING_4_MY_APP');
  bAppRunning:= (Mutex = 0) OR (GetLastError = ERROR_ALREADY_EXISTS);
  if not bAppRunning then
  begin
    Application.Initialize;
    Application.Title:=....
    Application.CreateForm(....);
    Application.Run;
  end;

Open in new window


This one will not allow to run second instance of application.
... similar code using Mutexes can be used for other lock things.
GrahamDLovellAuthor Commented:
Thanks for the info on Mutex, however, TStream does the task I want done quite adequately.

Further investigation has shown that I can create a new application with the following code:
  StreamData := TFileStream.create('chapters.cds',
      fmOpenReadWrite or fmShareExclusive);
  cds.LoadFromStream(StreamData);
  cds.SaveToStream(StreamData);

Open in new window

  StreamData := TFileStream.create('chapters.cds',
      fmOpenReadWrite or fmShareExclusive);
  cds.LoadFromStream(StreamData);
  cds.SaveToStream(StreamData);

Open in new window

StreamData := TFileStream.create('chapters.cds',
      fmOpenReadWrite or fmShareExclusive);
  cds.LoadFromStream(StreamData);
  cds.SaveToStream(StreamData);

It works fine, but when I use what I think is the same code in my larger (multi-unit) form, it doesn't work. That is, it does not write the file to disk.

Interestingly, in the small program, I can drill down in the code (while running, using F7) to the DataSnap.DBClient procedure TCustomClientDataSet.SaveToStream(Stream: TStream; Format: TDataPacketFormat = dfBinary); and see it in operation.

However, in the larger program, I cannot drill down (using F7) to expose that procedure and to watch it in operation.
GrahamDLovellAuthor Commented:
I found that before calling cds.SaveToStream(StreamData) I had to assign a starting position for the write operation. After that all is well:

cds.Position := 0;

Just one line of code, but endless grief!

Bliss!

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
GrahamDLovellAuthor Commented:
My original question was imprecise, which created some difficulties, for which I apologise. Other solutions were work-arounds. Only this fix actually solved the "clientdataset.saveToStream problem".
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.