OleGraphic.LoadFromStream() Raises OLE Error 800A01E1 - delphi

I have to load a jpeg file into OleGrafic. When I call OleGraphic.LoadFromStream(), it raises OLE Error 800A01E1.
I tried to identify this error, without any success.
The closest answer related to ole errors was List of all OLE error codes but I can not find 800A01E1 there.
I tried also to initialize Ole in main form. Same form that is generating this error. Without any success.
initialization
OleInitialize(nil);
finalization
oleuninitialize;
# Remy Lebeau: This code worked proper in other projects:
sFileName := OPD.FileName;
OleGraphic := TOleGraphic.Create;
fs := TFileStream.Create(sFileName, fmOpenRead Or fmSharedenyNone);
// fs.Seek(0,soFromBeginning);// := 0; Here I tried to make sure it's o.
OleGraphic.LoadFromStream(fs);
Source := TImage.Create(Nil);
Source.Picture.Assign(OleGraphic);
SrcBild := TBitmap.Create;
SrcBild.Width := Source.Picture.Width;
SrcBild.Height := Source.Picture.Height;
SrcBild.Canvas.Draw(0, 0, Source.Picture.Graphic);
DstBild := TBitmap.Create;
DstBild.Width := 200;
DstBild.Height := 100;
SmoothResize(SrcBild, DstBild);
SmoothResize is from http://www.swissdelphicenter.com/torry/showcode.php?id=1896

I cannot tell you why that image fails to load, but I can tell you the name of that error code, and how to find such codes in the future.
8-digit hexadecimal exception/hresult error codes in Windows can usually be found by prefixing them with 0x, in this case giving 0x800A01E1.
A simple search for that tells me that the name of that error code is CTL_E_INVALIDPICTURE, and that result is also listed on the underlying Win32 function, OleLoadPictureFile:
Return value
This method returns standard COM error codes in addition to the following values.
Return code Description
S_OK The method completed successfully.
CTL_E_INVALIDPICTURE Invalid picture file.
Of course, that doesn't tell you what is wrong either, but at least you now know that the likely source is the actual content of the file.
Since the documentation for the function also lists valid file formats, my guess would be to first ensure the file is one of those formats, and not just some other image file (or random data for that matter) masquerading as a JPEG or something under an incorrect filename, and then try to load that file up into some other program. Perhaps you can even just load and save the file in some tool that can rewrite the contents of image files, might fix it.

TOleGraphic.LoadFromStream() uses OleLoadPicture() internally. According to its documentation:
The stream must be in BMP (bitmap), WMF (metafile), or ICO (icon) format.
In other words, JPG is not supported.

Related

THandle of File in MemoryStream (Delphi)

I have a file loaded in a memorystream and want to get the filehandle of it without saving the file on the harddisk.
I can't figure out how to do it.
It should return the same result as CreateFile does.
myFile:= CreateFile('myfile.exe', GENERIC_READ, FILE_SHARE_READ...
I tried it with the Memory attribute of memorystream but it doesn't return the same handle as CreateFile
mem_stream := TMemoryStream.Create;
mem_stream.LoadFromFile('myfile.exe');
mem_hFile := mem_stream.Memory;
Writeln(Format('mem_hFile: %d', [mem_hFile]));
mem_stream.Free;
I have a file loaded in a memorystream and want to get the filehandle of it without saving the file on the harddisk.
There is no file handle in TMemoryStream. What you have done is load a copy of the file's bytes into a block of memory. The TMemoryStream.Memory property returns a pointer to that memory block.
It should return the same result as CreateFile does.
Then you have to actually call CreateFile(), either directly or indirectly, such as with a TFileStream:
fs_stream := TFileStream.Create('myfile.exe', fmOpenRead or fmShareDenyWrite);
fs_hFile := fs_stream.Handle; // <-- returns the HANDLE from CreateFile()
...
fs_stream.Free;
A memory stream has no file handle associated with it. You cannot use a memory stream with any function that expects a file handle.
Whatever your problem is, getting a file handle from a memory stream is, by dint of being impossible, not the solution.
You also seem to have a misunderstanding about the numeric values of handles. You cannot compare the numeric values of two handles and expect them to be the same value if the handles refer to the same object. Two distinct file handles have different numeric value, even if they refer to the same value.

Downloading list of files from remote FTP

I'm getting a problem using the TidFTP component.
I'm able to connect with the server using a code like this
vFileList := TStringList.Create;
oClientFTP := TidFTP.Create(nil);
oClientFTP.Port := PortFTP;
oClientFTP.Host := IPHost;
oClientFTP.UserName := UserFTP;
oClientFTP.Password := PasswordFTP;
After getting several files from the StringList (this one has exactly 778 elements) when the element no. 137 is retrieved the exception EIdAcceptTimeout is raised with "Accept timed out." message.
The code that I run is like this (runs in a Thread by the way)
procedure TDownloadFTP.Get;
begin
try
for I := 0 to vFileList .Count - 1 do
begin
sFileName:= vFileList [I];
posPoint := LastDelimiter('.', sFileName);
if posPoint = 0 then
ForceDirectories(ExtractFilePath(Application.ExeName) + '/BackUp/' + sFileName)
else
try
oClienteFTP.Get(sFileName,IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName) + '/BackUp/') + sFileName, True);
except
on E: EIdReplyRFCError do
begin
end;
on E: Exception do
exceptionList.Add(sFileName);
end;
end;
After the exception, the file is downloaded correctly but the process needs like 25 seconds per file (I'm downloading 2KB png images).
Any idea of the meaning of this Exception?
Thanks
Googling for EIdAcceptTimeout leads to this discussion in the Indy forum:
UseHOST in TIdFTP (client) => EIdAcceptTimeout
Where Remy Lebeau states:
The only time that exception can occur during a data transfer is if
you have the TIdFTP.Passive property set to False, which tells the FTP
server to make an inbound connection to TIdFTP. Those connections are
usually blocked by firewalls/routers that are not FTP-aware. You
usually have to set TIdFTP.Passive=True when you are behind a
firewall/router.
So, the solution could be for you to add a line
oClientFTP.Passive := True;
Btw. In your code snippets you have both oClientFTP and oClienteFTP. Adjust my suggestion if needed.
I would have written this as comments, rather than an answer, but comments are too limited. Please let me know and excuse me if I misbehave.
Looking at your code a second time raises a few questions. I see that the StringList can have both files (posPoint <> 0) and presumably directories (posPoint = 0). Is element 137 a file or directory and if a file, is it the first file after a new directory?
Do the entries in the StringList include the path they ought to have after '\backup\?
Assuming your application is a Windows application (since you don't say otherwise), When you create new paths, why do you use forward slashes (/) instead of backslashes () which is the path delimiter on Windows? Does your code even create subdirectories on Windows? Well, maybe crossplatform Delphi adjusts according to OS.
In the oClienteFTP.Get statement you say IncludeTrailingPathDelimiter even if you already have a slash as the trailing delimiter in '/backup/'.
You should never anymore use 'ExtractFilePath(Application.ExeName)' and subdirectories, as storage for data files.

How to resolve "Sevenzip: Error result (00000001) Incorrect function" using JCLcompression unit

I'm using JCL version 2.4.1.4571 with Delphi XE3 and have had no luck decompressing archives. I've downloaded the dll's from JEDI's site and also tried using the 7z.dll (32bit) from 7-zip.org but either way I get the same error when I try to "ExtractAll"
See function below:
function TForm1.Decompress(FileName, DestDir: String): Boolean;
var
archiveclass: TJclDecompressArchiveClass;
Myarchive: TJclDecompressArchive;
begin
Decompress := False;
// Filename = name.7z or name.zip (simple test zips nothing fancy)
// DestDir = fully qualified path to an existing directory
archiveclass := GetArchiveFormats.FindDecompressFormat(FileName);
Try
if Assigned(archiveclass) then
Begin
Myarchive := archiveclass.Create(FileName);
if (Myarchive is TJclSevenZipDecompressArchive) then
Begin
try
Myarchive.ListFiles; { Fails without doing this first }
{ ExtractAll (AutocreateSubDir) must be set true if arc has directories or it will crash }
Myarchive.ExtractAll(DestDir, True);
Decompress := True;
except on E: EJclCompressionError do
Begin
ShowMessage(e.Message);
End;
end;
End
Else
ShowMessage('Not supported by 7z.dll');
End;
Finally
MyArchive.Free;
End;
end;
When I execute the MyArchive.ExtractAll line, I get an exception Sevenzip: Error result (00000001) Incorrect function. I based my code on code from others here on StackOverflow. Am I missing something I need to do first or is this a bug? I've replaced the extract line with MyArchive.ListFiles and get the same error (I saw that in an example here; however, I've yet to divine the purpose of ListFiles.
Compiling to 32bit target.
Edit: Created a series of different types of archives using 7-zip and tried to decompress each with my program. The first thing I discovered is that if the archive contains directories of files, ExtractAll will crash if you don't set the second parameter to True. I then tested archives with different compression methods.
.7z archive using LZMA2 Ultra compression gives the Hresult = 1 error
.zip archive using LZMA Ultra compression gives the Hresult = 1 error
.zip archives using flavors of Deflate or deflate64 all work fine.
It appears that the library doesn't handle LZMA compression at all. Since it makes no sense that the 7z.dll can't handle it, I'm guessing the problem is with the JEDI JCL code. I need to be able to compress/decompress .7z and .zip's using LZMA with this library or I could have just used the built in zip stuff to begin with. Any further suggestions would be appreciated.
I think that is a JCL implementation bug. 7z use COM interfaces, and returns HRESULT codes. JCL attemptes to translate them into error messages using SysErrorMessase(), but AFAIK it works only for Win32 error codes, not HRESULT. That way the return code S_FALSE gets mapped to ERROR_INVALID_FUNCTION (see http://issuetracker.delphi-jedi.org/view.php?id=6348).
My guess is that a 7z call is returning S_FALSE for some reason, because it encounters some issue when decompressing - and the error is deceiving.
See also Error Handling in COM.
Further Googling on this problem turned up http://sourceforge.net/p/jcl/mailman/message/21371812/
It appears "FindDecompressFormat doesn't find archive format if file name is not in lower case."
I tried changing the string I was passing to lowercase and I successfully decompressed an LZMA archive.
archiveclass := GetArchiveFormats.FindDecompressFormat(lowercase(FileName));
JEDI JCL would be a cool library if it had any documentation whatsoever - sad.
If you call TJclZipDecompressArchive with a filename that does not exist you will get the same not very helpful error message on the ListFiles function.
Moral of the story check if the file exists yourself before calling the api.

Delphi - Using OLE causes error in Word

Delphi XE6. I have an application for text snippets, which are stored in a local Database (ABS DB). Some snippets may be straight text, others may include formatting. If the snippets include formatting, the snippets are MS Word format.
The user can view the snippets in two ways, inside my app, or by calling MS-Word, and having the snippet loaded there automatically, - IT IS THE SAME SNIPPET ..
Issue: If the snippet is loaded into a TOLEContainer in my app, it displays fine without a problem... If the snippet gets spawned off into MSWord, I get an error...
"We're sorry. We can't open because we found a problem with its contents." I click on OK, and then get "Word found unreadable content in . Do you want to recover the contents of this document?..." I clik OK, and everything displays fine.
My general processing flow for the "spawn off MS WORD" is...
// FN is a temp file name
FileStream := TFileStream.Create(FN, fmCreate);
BlobStream := dm_text.tEntries.CreateBlobStream(dm_text.tEntries.FieldByName('ANSWER_FMT'), bmRead);
FileStream.CopyFrom(BlobStream, BlobStream.Size);
BlobStream.Free;
FileStream.Free;
// Now open default association, which will be Word
ShellExecute(Handle, 'open', PWideChar(FN), nil, nil, SW_SHOWNORMAL);
This flow is nearly identical for the In place viewing... other than a few commands for the TOleContainer.
OleWord.Enabled := True;
FileStream := TFileStream.Create(FN, fmCreate);
BlobStream := tEntries.CreateBlobStream(tEntries.FieldByName('ANSWER_FMT'), bmRead);
FileStream.CopyFrom(BlobStream, BlobStream.Size);
BlobStream.Free;
FileStream.Free;
OleWord.LoadFromFile(FN);
OleWord.DoVerb(ovInPlaceActivate);
Any ideas why this is happening? This happens on MULTIPLE versions of MSWord.
ADDITIONAL INFO:
Both routines look at the same data, pulled from the exact same row/column in my DB. What I do is create a TEMP file, and then load either via TOleContainer, which loads it fine, or via ShellExecute, which gives an error. However, if I manually load the TEMP file for the OLE Container into MSWord, I get the same error.
So - possibilities...
1). My data is corrupted, i.e. how I save it is wrong...but Word can correct it.
2). I have a setting somehow so that OLEContainer doesn't show the error but Word does.
It is because when using OleContainer.SaveToFile or SaveAsDocument, you are not creating a docx file, but an OleObject containing a docx file. When using OleContainer.SaveToFile with UseOldStreamFormat = True, there is even a Delphi specific header added. Word fortunatly detects this and gives you the option to restore the file.
If you want a valid word-document, then activate the OleContainer (OleContainer.DoVerb(ovPrimary) and then save the document via Word itself (OleContainer.OleObject.SaveAs(MyFileName, wdFormatDocument, EmptyParam, EmptyParam, False).
After that you can store the resulting file in your database.

How can a message client read an attachment downloaded by indy?

I have a message client, written in delphi using Indy libraries, that receives email messages. I am having difficulties decoding an MMS text message email.
These messages come as multipart/mixed emails with one message part (an attachment) that of text/plain (that is base64 encoded) with a filename like text0.txt.
My TIdMessageClient calls ProcessMessage (using the stream-based version) to populate a TidMessage that I'm going to display on the screen. But as I go through the message parts and try to unravel them, that attached file is a thorn in my side. Currently, I have it printing out the name of the attachment into a string which works fine (see code snippet below, FBody is a string type), but can't get the text file's contents.
Here's the bit that does work:
FBody := 'Attachment: ['+TidAttachment(Msg.MessageParts.Items[0]).FileName+']';
(Edited:) Originally when I wrote this question I wasn't sure if the attachment was stored in a TidAttachmentFile or TidAttachmentMemory object. But with the right debugger commands, I've determined it's a TidAttachmentFile. I suppose it would be possible to use TidAttachmentFile.SaveToFile() to save the attachment to a file on disk and then read the file back from disk, but that seems wasteful and slow (especially for a 200 character text message). I would really prefer to do this all "in memory" without temp files if possible.
What do I need to do (a) make TidMessageClient return a TidAttachmentMemory object rather than a TidAttachmentObject (in ProcessMessage), and (b) read the attached text file into a string?
Based on the indy documentation, the start I have at how this code would look is roughly like this:
TidAttachmentMemory(Msg.MessageParts.Items[0]).PrepareTempStream();
FBody := FBody + TidAttachmentMemory(Msg.MessageParts.Items[0]).DataString;
TidAttachmentMemory(Msg.MessageParts.Items[0]).FinishTempStream;
Please feel free to point me in the right direction if this is not the right way to go or use TidAttachment(s).
I suppose it would be possible to use TidAttachmentFile.SaveToFile() to save the attachment to a file on disk and then read the file back from disk, but that seems wasteful and slow (especially for a 200 character text message).
When using TIdAttachmentFile, the file is always on disk. The TIdAttachmentFile.StoredPathName property specifies the path to the actual file. The TIdAttachmentFile.SaveToFile() method merely copies the file to the specified location.
I would really prefer to do this all "in memory" without temp files if possible.
It is possible.
What do I need to do (a) make TidMessageClient return a TidAttachmentMemory object rather than a TidAttachmentObject (in ProcessMessage)
In the TIdMessage.OnCreateAttachment event, return a TIdAttachmentMemory object, eg:
procedure TMyForm.IdMessage1CreateAttachment(const AMsg: TIdMessage; const AHeaders: TStrings; var AAttachment: TIdAttachment);
begin
AAttachment := TIdAttachmentMemory.Create(AMsg.MessageParts);
end;
If no handler is assigned to the TIdMessage.OnCreateAttachment event, or if it does not assign anything to AAttachment, then a TIdAttachmentFile is created by default.
You could optionally implement your own custom TIdAttachment-derived class instead, say one that uses TStringStream internally if you know the attachment contains textual data (which the AHeaders parameter will tell you).
and (b) read the attached text file into a string?
Based on the indy documentation, the start I have at how this code would look is roughly like this:
You are close. You need to use the TIdAttachment.OpenLoadStream() method instead of TIdAttachment.PrepareTempStream(), and you need to read the data from the TStream that TIdAttachment.OpenLoadStream() returns. In your example, you could use Indy's ReadStringFromStream() function for that, eg:
// if using Indy 10.6 or later...
var
Attachment: TIdAttachment;
Strm: TStream;
begin
...
Attachment := TIdAttachment(Msg.MessageParts.Items[0]);
Strm := Attachment.OpenLoadStream;
try
FBody := FBody + ReadStringFromStream(Strm, -1, CharsetToEncoding(Attachment.Charset));
finally
Attachment.CloseLoadStream;
end;
...
end;
Or:
// if using Indy 10.5.x or earlier...
var
Attachment: TIdAttachment;
Strm: TStream;
Enc: TIdTextEncoding;
begin
...
Attachment := TIdAttachment(Msg.MessageParts.Items[0]);
Strm := Attachment.OpenLoadStream;
try
Enc := CharsetToEncoding(Attachment.Charset);
try
FBody := FBody + ReadStringFromStream(Strm, -1, Enc);
finally
Enc.Free;
end;
finally
Attachment.CloseLoadStream;
end;
...
end;

Resources