ZipForge Native Errors - delphi

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

Related

What could cause an unexpected 'File In Use' Error

I have a Delphi5 application which exports a file (.pdf) and a very small metadata file to a network location. The intention is that these 2 files should be processed, and then removed, by a polling .NET application.
My approach is to
Write the metadata file with the extension '.part'
Generate the .pdf
Rename the .part file to .dat
The .NET process is looking for files with the extension '.dat' only, so I would expect there to be no conflict between the 2 reader/writers. However, the .NET process is occasionally logging the following error ...
System.IO.IOException: The process cannot access the file '\\server\Path\FileName.dat' because it is being used by another process.
(I say occasionally - we are currently testing, so when volumes increase this may become much more of an issue)
The Delphi code looks like this :
AssignFile(FTextFile, Format('%s\%s.part', [DMSPath, FullFileName]));
try
try
ReWrite(FTextFile);
Writeln(FTextFile, MetaDataString);
finally
CloseFile(FTextFile);
end;
except
raise ELogFileException.Create( LOGFILEWRITEFAILURE );
end;
Then there is a separate method which performs the following lines of code
if FindFirst(Format('%s\*.part',[DMSPath]), faAnyFile, SearchRec) = 0 then begin
repeat
OldName := Format('%s\%s',[DMSPath, SearchRec.Name]);
NewName := Format('%s\%s',[DMSPath, ChangeFileExt(SearchRec.Name, '.dat')]);
RenameFile(OldName, NewName);
until FindNext(SearchRec) <> 0;
FindClose(SearchRec);
end;
I cannot see anything inherently wrong with this code and we have a couple of remedies in mind, but I have 2 questions
Should I try a different technique to more reliably protect the '.dat' file until it is fully ready
What circumstances could be causing this?
So far there has been one suggested cause - Antivirus software.
Any suggestions as to how the file might be produced differently? Note that my application is Delphi5; I wondered if there was a newer, more 'atomic' version of the 'MoveFileA' WinApi call I could use.
In the past, we had a problem with file being locked like that. Investigation pointed to Windows' Prefetch. The file being affected were not on a network directory though.
As far as I know, Prefetch only work on process startup and/or while booting (Controlled by a registry key), so it might not apply to your current situation.
You can check the "C:\"Windows"\Prefetch\" directory. If prefetch is active, it should contains multiple *.pf files. If there is one with your executable filename, it might be worth investigating.
Personally speaking, because there are multiple files involved, I'd create a separate lock file (eg, myfile.lck) that you write first. If the polling app sees it in the folder, then it stops looking for other files. Once that file is gone, then it dives deeper. I don't know if this would solve the problem you're encountering or not, but I'd give it a try. (Files with .dat extensions are frequently created by malicious evildoers, so they can raise spurious issues through other sources, like AV software. A lock file with 0 bytes in it is generally harmless and disregarded.)

Why is COMMON_APPDATA returned as a null string on Windows XP

One of my users at a large university (with, I imagine, the aggressive security settings that university IT departments general have on their computers) is getting an empty string returned by Windows XP for CSIDL_COMMON_APPDATA or CSIDL_PERSONAL. (I'm not sure which of these is returning the empty string, because I haven't yet examined his computer to see how he's installed the software, but I'm pretty sure it's the COMMON_APPDATA...)
Has anyone encountered this or have suggestions on how to deal with this?
Here's the Delphi code I'm using to retrieve the value:
Function GetSpecialFolder( FolderID: Integer):String;
var
PIDL: PItemIDList;
Path: array[0..MAX_PATH] of Char;
begin
SHGetSpecialFolderLocation(Application.Handle, FolderID, PIDL);
SHGetPathFromIDList(PIDL, Path);
Result := Path;
end; { GetSpecialFolder }
ShowMessage(GetSpecialFolder(CSIDL_COMMON_APPDATA)); <--- This is an empty string
Edit:
Figuring out this API made me feel like I was chasing my tail - I went in circles trying to find the right call. This method and others similar to it are said to be deprecated by Microsoft (as well as by a earlier poster to this question (#TLama?) who subsequently deleted the post.) But, it seems like most of us, including me, regularly and safely ignore that status.
In my searches, I found a good answer here on SO from some time ago, including sample code for the non-deprecated way of doing this: what causes this error 'Unable to write to application file.ini'.
If you want to find out why an API call is failing you need to check the return values. That's what is missing in this code.
You need to treat each function on its own merits. Read the documentation on MSDN. In the case of SHGetSpecialFolderLocation, the return value is an HRESULT. For SHGetPathFromIDList you get back a BOOL. If that is FALSE then the call failed.
The likely culprit here is SHGetSpecialFolderLocation, the code that receives the CSIDL, but you must check for errors whenever you call Windows API functions.
Taking a look at the documentation for CSIDL we see this:
CSIDL_COMMON_APPDATA
Version 5.0. The file system directory that contains application data for all users. A typical path is C:\Documents and Settings\All
Users\Application Data. This folder is used for application data that
is not user specific. For example, an application can store a
spell-check dictionary, a database of clip art, or a log file in the
CSIDL_COMMON_APPDATA folder. This information will not roam and is
available to anyone using the computer.
If the machine has a shell version lower than 5.0, then this CSIDL value is not supported. That's the only documented failure mode for this CSIDL value. I don't think that applies to your situation, so you'll just have to see what the HRESULT status code has to say.

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

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.

Unit source code does not match code execution path when breakpoint hit

I am debugging a DirectShow filter I created with the DSPACK code library using Delphi 6 Pro. When a breakpoint I set is hit in one particular unit named BaseClass.pas, and I begin tracing, the Execution Point jumps to strange places in the source code. This usually indicates that the source code being traced does not match the source code that was compiled into one of the packages being used by the Delphi application. Oddly enough it is only the BaseClass unit since I have traced other units belonging to the DSPACK code library and they do not exhibit this problem. I am not using run-time packages.
I scanned my disk and found only one copy of BaseClass.dcu with a modification date equal to the last time I built the program. I have not modified the source for that unit or any other belonging to DSPACK. Since my Filter is part of the main application this indicates that BaseClass.pas would be subject to a dual use situation since it is used to build the DSPACK component package (dpk), and is also referenced by my main application directly via the TBCSource object my Filter descends from. Note, I did try adding the unit PAS file directly to my Project but that didn't fix anything.
I also went back and re-opened each of the DSPACK package files and did a full re-build. None of this helped. Is there something else I can try to get the source synchronized with the compiled image of the BaseClass unit? Or is a different problem altogether and if so, what is it and how can I fix it?
Sometimes this happens when code is copied/pasted from web pages or other sources, and the lines don't end with CR/LF pairs (#13#10 or 0x0D0A, standard for Windows) but end in only LF (#10 or 0x0A, typically the line ending in *nix systems) or CR (#13 or 0x0D, typical with Mac OSX/iOS). The incorrect line terminators confuse the debugger - this has been an issue for the past several Delphi versions.
You can sometimes fix this by opening the source file using a text editor like Notepad, making a small meaningless change (insert and then delete a blank line, for instance), and then save the file.
I had same problem and made a similar utility. Fixed it.
Basically, just this:
procedure adjustCRLF(filename : String);
var
strList : TStringList;
begin
strList := TStringList.Create;
try
strList.LoadFromFile(filename);
strList.Text := AdjustLineBreaks(strList.Text);
strList.SaveToFile(filename);
finally
strList.Free;
end;
end;
There is another way this can happen: if the IDE erroneously opens another source file with the same name (but different, such as an earlier version) then all the debug points will be incorrect, and the debugger will even allow you to step through the incorrect file.
I've seen Delphi 7 do this once.
Make sure that when you rebuild it, that in the compiler options for your project that you have "Debug Information" turned on. In fact, most of the options under Debugging should be set in your project's Compiler options.
Also, if you haven't already, restart Delphi.

Getting an error with excel ole "Add method of Workbooks class failed"

I am trying to open and refresh an excel 2003 spreadsheet via ole. However I am getting the error "Add method of Workbooks class failed" with no further information.
The code works in unit tests, and works on 3 servers but fails with the error on our web server. It is being run from a service app running under the Local System Account.
The same version of excel is installed on all servers (2003 sp3). The file(s) in question all exist and are at the expected location.
There are no macros in the spreadsheets, but there are database queries. The spreadsheets can all be opened.
The calling code is
if VarIsEmpty(XLApp) then
begin
XLApp := CreateOleObject('Excel.Application');
try
XLApp.DisplayAlerts:= wdAlertsNone;
except
...
end;
XLApp.AutomationSecurity:= msoAutomationSecurityForceDisable;
end;
fullFileName:= ExpandReportFileName( partialFilename);
if not FileExists(fullFileName) then
raise Exception.Create('File not found: ' + fullFileName);
XLAPP.Workbooks.Add(fullFileName); << fail here
Any ideas on what else I can try?
I had been getting the same error for
xls.WorkBooks.Add(xlWBATWorksheet);
I changed that line to
xls.Application.Workbooks.Add;
Now it is working. You might try to get a Workbook first then try to call its methods.
Automating Office applications in a service is not supported.
While it is possible, it's very difficult, and you'll run into many problems, such as this one. It'll also be very slow.
You should look for a Delphi component that manipulates Excel files.
Depending on what you're trying to do, you might be able to use OLE DB instead.
Workbooks.Open might be the method you're looking for
Add creates a new empty workbook. If you supply a filename, that file is used as a template for the new file - see here
Open just opens the file as you would expect - see here
I ran into this error when I happened to have the Excel spreadsheet file selected in a Windows Explorer window, not open, just selected. When I deselect the file, I do not get the error.

Resources