Advantage Table File in use error. How can I resolve? - delphi

I'm having trouble getting a certain table to open up in more then one instance of my program.
Whats happening is I'm trying to allow users to open up and replace a current table(part of a data dictionary - FileForm.ImagesTable) with an older table (not included in the data dictionary). It works great for one instance of the program but when we try to open up that same file simultaneously on another instance. I get the following error.
FileName.ADT This file is in use. Enter a new name or close the file that's open in another program.
Below is the code I have reassigning the table name and datapath to the selected table.
OpenDialog1.FileName := '*.adt';
OpenDialog1.Filter := 'Software 6.0 Files (*.adt)|*.adt|Software 5.x Files (*.dbf)|*.dbf';
OpenDialog1.InitialDir := DataPath;
if OpenDialog1.Execute then
begin
Str1 := Trim(OpenDialog1.FileName);
if Length(Str1) = 0 then
Exit;
DSImage.Enabled := False;
with FileForm.ImagesTable do
begin
Active := False;
AfterOpen := FileForm.TableOther.AfterOpen;
DataBaseName := ExtractFilePath(Str1);
TableName := ExtractFileName(Str1);
Active := True;
end;
end;
Edit * Using Advtantage 8.1, Seems to be a windows error because the error happens in the dialogue window. And yes Exclusive is set to false.
Any thougths on why this is happening and how this could be resolved are appreciated.
Thanks

You're not clear on the specific error - is it a Windows error or an Advantage error?
If it's a Windows error, it may be because you've specified exclusive access to the table (ImageTable.Exclusive = True). This would mean that the first instance of the app could open it, but subsequent tries would fail with a File is in use error.
If it's an Advantage error, the Advantage help file (here in v11's documentation, since you didn't specify a version of ADS - note it's in a frame, so you may need to use this link, navigate to Advantage Developers Guide, expand the Part 1->Chapter 4 - Dictionaries->Understanding Dictionaries topic) says:
A data dictionary is a special file that serves as the sole access point for database tables
Note the sole access point. Once a table is in the data dictionary, it belongs to the data dictionary. You're trying to replace that reference with something outside the scope of the dictionary, and that isn't allowed. I'm pretty sure that the problem is related to that - ADS puts a proprietary lock on tables that are included in the dictionary, and controls access to those files through the server by way of the dictionary.
You'll need to either remove the table from the dictionary and use it as a free table, or come up with a different strategy for removing the current data and replacing it with other data to preserve the integrity of the dictionary.

It looks like you are only using the Open Dialog to get the name of the table.
On the Open dialog try setting the option ofShareAware
OpenDialog1.Options := OpenDialog1.Options + [ ofShareAware ];
Once the table is open with Advantage the mode is both deny write, deny read and as a result will return a sharing error if anything non-advantage tries to open the table.

Related

Parameter value from TUIBQuery gets truncated when read back for Database.Charset=csUTF8

I am working on an application built in Delphi 2010 that uses UIB to connect to a Firebird 2.5 database. The application has been running using the default character set for a long time, i.e. nobody gave character sets any special thought and it has simply been working. Currently I am trying to make it work correctly with UTF-8 data.
In doing so I have hit upon a problem with TUIBQuery and parameterized queries. When using Database.Charset=csUTF8 and setting a parameter value for a CHAR(n)-field and retrieving it before executing the query the value is truncated.
Unfortunately some of my code writes and reads parameter like this in a number of places and therefore dies an ugly death.
To isolate and demonstrate the problem I created a simple fresh database with DEFAULT CHARACTER SET UTF-8 and a table like this:
CREATE TABLE TEST (
CHARFIELD CHAR(20),
VARCHARFIELD VARCHAR(20)
);
I set up the application to connect to the database using TUIBDatabase and TUIBTransaction. Then I created a TUIBQuery-instance, set SQL to a parameterized INSERT-statement into this table, and set the parameters:
Query := TUIBQuery.Create(NIL);
Query.Transaction := Transaction;
Query.SQL.Text := 'INSERT INTO TEST (CHARFIELD, VARCHARFIELD) VALUES (:CHARFIELD, :VARCHARFIELD)';
Query.Prepare(True);
s:= 'ABC';
Query.Params.ByNameAsString['CHARFIELD'] := s;
Query.Params.ByNameAsString['VARCHARFIELD'] := s;
When I now read the parameter-values back like this:
s := Query.Params.ByNameAsString['CHARFIELD'];
s := Query.Params.ByNameAsString['VARCHARFIELD'];
The results are correct for Database.Charset=csNone. But when I instead specify DataBase.Charset=csUTF8 the value for CHARFIELD is truncated to 'A' instead of 'ABC'. The value for VARCHARFIELD is fine. The behaviour is independent of the actual data, I do not have to actually use non-ASCII-characters to provoke it, as the sample shows.
Calling ExecSQL() on the query works correctly and INSERTs the data as expected in both cases.
I have uploaded sourcecode to my simple test program as UIB_UTF8_Test.zip.
Does someone here have any idea what I may be doing wrong and how to do it right?

Imap4 client command LSUB

I have a problem with function TIdIMAP4.ListSubscribedMailBoxes(AMailBoxList: TStrings): Boolean; with this implementation :
function TIdIMAP4.ListSubscribedMailBoxes(AMailBoxList: TStrings): Boolean;
begin
{CC2: This is one of the few cases where the server can return only "OK completed"
meaning that the user has no subscribed mailboxes.}
Result := False;
CheckConnectionState([csAuthenticated, csSelected]);
SendCmd(NewCmdCounter, IMAP4Commands[cmdLSub] + ' "" *',
[IMAP4Commands[cmdList], IMAP4Commands[cmdLSub]]); {Do not Localize}
if LastCmdResult.Code = IMAP_OK then begin
// ds - fixed bug # 506026
ParseLSubResult(AMailBoxList, LastCmdResult.Text);
Result := True;
end;
end;
When I debug I see that the LastCmdResult.Text stringlist is empty, but the LastCmdResult.FormattedReply stringlist has all folders on my email server (Inbox, Sent, Trash, ...). When I tried to use LastCmdResult.FormattedReply count or text, it had immediately lost its data and gave LastCmdResult.FormattedReply.Count=0 and LastCmdResult.FormattedReply.Text=''. So I'd like to know if there is a way to enter the data inside LastCmdResult.FormattedReply and get my email server folders or there is another way to solve my problem ?
I have a problem with function TIdIMAP4.ListSubscribedMailBoxes(AMailBoxList: TStrings): Boolean; with this implementation :
Works fine for me when I try it using the latest SVN version of Indy.
When I debug I see that the LastCmdResult.Text stringlist is empty, but the LastCmdResult.FormattedReply stringlist has all folders on my email server (Inbox, Sent, Trash, ...).
When I run it, the opposite happens. LastCmdResult.Text contains the expected text, and LastCmdResult.FFormattedReply is empty (notice I mention the FFormattedReply data member directly, see below).
When I tried to use LastCmdResult.FormattedReply count or text, it had immediately lost its data and gave LastCmdResult.FormattedReply.Count=0 and LastCmdResult.FormattedReply.Text=''.
That is by design. The FormattedReply property is intended to be used by a client to parse a server reply so it can populate TIdReply's property values, and to be used by a server to generate a new reply using TIdReply's property values. So, you cannot read from the FormattedReply property on the client side.
So I'd like to know if there is a way to enter the data inside LastCmdResult.FormattedReply and get my email server folders or there is another way to solve my problem ?
The whole purpose of ListSubscribedMailBoxes() is to return the folder names in the AMailBoxList parameter. If that is not working for you, then either
you are using a older/buggy version of Indy.
your server is sending the data in a format that TIdIMAP4 is not able to parse.
Without knowing which version of Indy you are actually using, or what the server's reply data actually looks like, there is no way to diagnose your issue one way or the other.

Access violation error using DOM Delphi 7

I'm building a software to process orders on a site. I'm using DOM to navigate through the website and I want my app not to hang when I use a command on the "wrong" page.
Ex:
try
WebBrowser.OleObject.Document.GetElementByID('ContentPlaceHolder1_txtCommande').setAttribute('value', lblDate.Caption);
except
end;
All I want is for the error to be ignored if the field is not found as this error is not important. Thank you!
Edit: wrote ADO when I meant DOM
You yourself say that the element might not be found, so what do you expect
WebBrowser.OleObject.Document.GetElementByID('ContentPlaceHolder1_txtCommande')
to return other than nil in that case? Yet you "blatantly" use whatever is returned by appending
.setAttribute('value', lblDate.Caption);
to it.
Change your code to
var
element: IDomElement; // Whatever it should really be
begin
element := WebBrowser.OleObject.Document.GetElementByID('ContentPlaceHolder1_txtCommande');
if Assigned(element) then
element.setAttribute('value', lblDate.Caption);
Edit
if you are working with variants instead of using msxml through its type library or some other xml library in which you can find the proper type returned by GetElementID, then, as #GerryColl mentions, you can use element: OLEVariant and check for NULL instead of for a nil pointer.

ZipForge Native Errors

The archives I'm having trouble with were all created by merging a working archive with a non-existant archive, thereby effectively copying the contents of one into the other. It's part of a merging process we do. Like this...
ZipDestination := TZipForge.Create(nil);
if FileExists(DestinationZipFileName) then
ZipDestination.OpenArchive(fmOpenReadWrite + fmShareDenyWrite)
else
ZipDestination.OpenArchive(fmCreate);
ZipDestination.Zip64Mode := zmAuto;
ZipDestination.MergeWith(SourceZipFileName);
ZipDestination.CloseArchive;
and this is the code that gets a blob from the archive, uncompresses it, and makes it ready for the viewer.
CompressedStream := TMemoryStream.Create;
UnCompressedStream := TMemoryStream.Create;
GetCompressedStream(CompressedStream); // this fetches the blob from the zipfile
ZipForge.InMemory := True;
// Native Error 00035 on next line (sometimes)
ZipForge.OpenArchive(CompressedStream, False);
ZipForge.FindFirst('*.*', ArchiveItem, faAnyFile - faDirectory);
sZipFileName := ArchiveItem.FileName;
sZipPath := ArchiveItem.StoredPath;
ZipForge.ExtractToStream(sZipPath + sZipFileName, UnCompressedStream);
ZipForge.CloseArchive;
but I'm encountering "Native error 00035" sometimes.
Now the strange thing is that I'm getting these errors when I try to view the first blob within the merged archive (ie. trying to view other blobs within the merged archive doesn't raise any exception)
It could be something about ZipForge.MergeWith that I haven't catered for, or it could be a bug in my GetCompressedStream (but if I switch the order of blobs within the archive, it always happens to the first one only). Look like it's time for a test project to see what's really going on.
EDIT
Original question was simply asking for guidance on these Native Errors, for which I'm satisfied with the answer I've chosen. As for my problem, well I'm convinced it's an issue with the CompressedStream I'm passing into OpenArchive.
Native error 00035 is "Invalid archive file". It occurs when ZipForge can't find either the local or central directory headers (that is, when you try to open a file that isn't a zip).
I don't think they're documented in the help, but the translation tables for native error to error code occur in ZFConst.pas. There's a NativeToErrorCode table that converts from the "native" error into an index in the error string array. If that isn't enough to tell you what the problem is just look through ZipForge.pas for the error code in a raise statement. They consistently use the full 5-digit code, so you can search for 00035 instead of just 35 to avoid spurious results.
Free support offered from the ZipForge vendor http://componentace.com/help/zf_guide/gettinghelpfromtechnicalsu.htm

TWordApplication and Word collision

I am using TWordApplication in Delphi. My app opens new instance of word and make something on its document. Problem is when i first run my app and next open real word exe. Word exe didnt open new word instance but it link to my app instance. So when my app write to its document all text appears on exe word visible to user.
WordApp := TWordApplication.Create(nil);
WordApp.ConnectKind := ckNewInstance;
(WordApp.Documents.Add(EmptyParam,EmptyParam,EmptyParam, varFalse ));
Then the user opens Word manually.
WordApp.Selection.Text := 'test test test';
And user see 'test test test' in manually opened Word.
If i first opens Word manually and starts my app all is ok.
This is default behaviour of Word, it uses a running instance. What you have to do is store a reference to the document you want to modify. So don't use ActiveDocument, but use the Document you stored. Because there is no guarantee that ActiveDocument is the document you think it is.
//starting Word
var
App: TWordApplication;
Doc: WordDocument;
begin
App := TWordApplication.Create(nil);
Doc := App.Documents.AddOld(EmptyVar, EmptyVar); //open new document
<..somewhere else..>
//modifying Word
Doc.DoWhateverIWant; // <--see? no ActiveDocument, so you are not
// modifying the users doc
Make sure you use
WordApp.ConnectKind := ckNewInstance;
to open your word application. Either do it in code (as above) or set the property at design time. This ensures that you are always running a new instance of Word and that it remains hidden unless you explicitely make it visible. Any user opening Word will then always get a different instance of Word and will not see what you have put on the document (unless you have saved it and they open the saved document).
From the doc:
Set ConnectKind to indicate how the
ConnectKind component establishes a
connection. ConnectKind establishes
the connection when the application is
run (if AutoConnect is True true) or
when the application calls the Connect
(or ConnectTo) method.
The following table lists the possible values:
//Value Meaning
//ckRunningOrNew Attach to a running server or create a new instance of the server.
//ckNewInstance Always create a new instance of the server.
//ckRunningInstance Only attach to a running instance of the server.
//ckRemote Bind to a remote instance of the server. When using this option,
// you must supply a value for RemoteMachineName.
//ckAttachToInterface Don't bind to the server. Instead, the application supplies an
// interface using the ConnectTo method, which is introduced in
// descendant classes. This option can't be used with the AutoConnect
// property.
Update
Actually, opening Word may have opened a different instance (that's how I remember it for D5/Word97), but at the moment Word does indeed re-use the instance opened by the application. So to avoid "scratching all over a word document manually opened by a user" you really do need to avoid using ActiveDocument as per The_Fox's answer.
According to http://support.microsoft.com/kb/210565 there are several command line switches that will cause Word to start a new instance. The one I have used with Word2003 is /x

Resources