THandle of File in MemoryStream (Delphi) - 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.

Related

OleGraphic.LoadFromStream() Raises OLE Error 800A01E1

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.

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;

How can I download a huge file via TIdHTTP?

I use this code to download small files:
Var
ms:TMemoryStream;
begin
ms:=TMemoryStream.Create;
Idhttp1.get('http://mydomain.com/myfile.zip',ms);
ms.SaveToFile('myfile.zip');
ms.Free;
end;
But file is saved in RAM before storing to disk, so it may be difficult to download files >1Gb, for example. Is there a way to download a file by its parts? Or do I need to use the WinInet? Thanks in advance!
TMemoryStream provides an in-memory buffer, so if you download into one, you need to have enough memory to hold everything you receive. It's not the only kind of stream, though. You can pass the Get method any kind of stream you want, including one that writes its contents to disk as it receives it. Use TFileStream, for example.
var
s: TStream;
s := TFileStream.Create('myfile.zip', fmCreate);
try
IdHttp1.Get(..., s);
finally
s.Free;
end;
Anywhere you call LoadFromFile or SaveToFile on a TMemoryStream, it's possible that TFileStream is a better choice.

Delphi Determine filesize in real time

Is it possible in Delphi to determine the size of a file as it is being copied? I get a notification when a file is first copied to a folder, but need to wait until the copy is complete before I can process the file.
I've used JclFileUtils.GetSizeOfFile(Filename) but that gives me the 'expected' file size, not the current filesize.
Regards, Pieter
Prompted by the first answer I decided to give up on trying to determine when a file copy has completed. Instead I found that using TFileStream gave me a reliable indication whether a file is in use or not.
function IsFileInUse(Filename: string; var ResultMessage: string): boolean;
var
Stream: TFileStream;
begin
Result := True;
ResultMessage := '';
try
Stream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
try
Result := False;
finally
FreeAndNil(Stream);
end;
Except on E: Exception do
ResultMessage := 'IsFileInUse: ' + E.Message
end;
end;
In this way I can keep on checking until the file is not in use anymore before attempting to process it.
It depends on the technique that is used by the copying function. Most copy-methods will allocate the disk space first before they start to copy a file. Thus, if you want to copy a file of 4 GB, the system starts by creating a file with random data for 4 GB in total. (Which is done lightning-fast, btw.) It then copies the data itself, but the file size is already what you expect.
This has as advantage that the sysmen can check if there's enough disk space available to actually copy the data.
If you write your own file copy function then you can have total control over how it does this. Else, you're limited to whatever the chosen copy-method offers you. So, how do you copy a file?
If you have control over the file copy process, it is easiest to have the copy routine create the file using a temporary filename, and when done, rename it to correct filename.
That way, you can use Windows folder monitoring to watch for the renaming (JCL contains a component to help with this, not sure about the name from here). When your code gets triggered you are sure the other side has finished writing the file.
A simple trick I used was to have the copying process create new files with a '$$$' extension. My code still got triggered for those but I ignored them until they were renamed to their proper filename.
Hope this helps.

Howto create threadsave a file without overwriting an existent one?

until now, whenever I wanted to create a file without overwriting an existing one, I've done something like this:
if not FileExists(filename) then
stream := TFileStream.Create(filename, fmCreate);
But that's not threadsafe. So now I am looking for a threadsafe version.
Perhaps I can combine some modes so that TFileStream.Create(filename, fmCreate +fm???); fails if the file exists?
I need this to communicate directory-locks with old DOS programs. But the DOS programs don't hold the files opened. :-(
TFileStream's file-name based constructor relies on the WIndows API call CreateFile to create the file handle that'll be used to access the file. The API itself has multiple parameters, and especially interesting for you is the Create Disposition: if you specify CREATE_NEW, the function fails if the file already exists. You can take advantage of that by calling CreateFile yourself, then using the returned handle to create the TFileStream. You can do that because TFileStream inherits from THandleStream, inherits it's handle-based constructor and owns the handle (calls CloseHandle on the handle you pass to the constructor).
Since this relies on the OS-provided function CreateFile, it'll be trehad-safe (no race condition between FileExists() and actually creating the file. It also blocks the old application from accessing the newly crearted file until you actually close the handle.
var FH: NativeUInt;
// ...
FH := CreateFile('FileName', GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_NEW, 0, 0);
if FH = INVALID_HANDLE_VALUE then
begin
// Creating the new file failed! I'm raizing an exception, but you can do something
// better-suited for your case, like trying a new file name.
RaiseLastOSError;
end;
// Create the stream using the prepared Handle
HS := TFileStram.Create(FH);
I would keep the FileExists check because it handles most cases without relying on exception handling. For the border cases you have to handle exceptions in TFileStream constructor properly. The second thread trying to create the file should fail if you use it with share mode fmShareDenyWrite.

Resources