How to insert and load TFileStream with TmongoWire - delphi

I use Delphi and TmongoWire. I would like to insert TFileStream (picture, pdf) with TmongoWire on a MongoDb server. My work is based on this demo file: Delphi MongoDB driver
d:=BSON([
'id',mongoObjectID,
'name',ItemForm.txtName.Text,
'address',ItemForm.txtAddress.Text,
'phone',ItemForm.txtPhone.Text,
'picture', TFileStream.Create('C:\temp\mongotest.jpeg', fmOpenRead or fmShareDenyWrite)
]);
FMongoWire.Insert(mwx1Collection,d);
//LoadItems;
LoadItem(ListView1.Items.Add,d);
UpdateCount;}
Inserting a stream file does not work, can somebody see why ?
The program does not compile : Error --> BSON can not be called with these arguments
There is no problem to insert a simple text field (code below) :
d:=BSON([
'id',mongoObjectID,
'name',ItemForm.txtName.Text,
'address',ItemForm.txtAddress.Text,
'phone',ItemForm.txtPhone.Text
]);
Thanks

Please refer to this tutorial: http://owlyci.com/docs/articles/WorkingWithGridFS
In GridFS files are referenced by names (or _id in rare cases), so you should save streams to GridFS with some filenames (aaa.jpg, 81276482634823.dat or any other), and then store this filenames to your collection
d:=BSON([
'id',mongoObjectID,
'name',ItemForm.txtName.Text,
'address',ItemForm.txtAddress.Text,
'phone',ItemForm.txtPhone.Text,
'picture', 'aaa.jpg'
]);

MongoDB has it's own sub-system specifically to store (large) files: GridFS
see more here: http://docs.mongodb.org/manual/core/gridfs/
and for a TMongoStream object that uses this: https://github.com/stijnsanders/TMongoWire/blob/master/mongoStream.pas
(For the sake of completeness: Delphi doesn't know by itself how to convert a TFileStream to a Variant, which causes the error.)

Related

Read/Write file from a .zip archive

Should I work with files within the archive file. (Read - write). By the following code, I get a list of files on my needs.
Zip := TZipFile.Create;
try
Zip.Open(FilePath, TZipMode.zmRead);
For File_Name in Zip.FileNames do
begin
//some code
end;
finally
Zip.Close;
FreeAndNil(Zip);
end;
I used the TZipFile.Read method to reads a file from a .zip archive .
This method returns the complete content of file into a buffer of type TByte. But just need to read a 1MB file from the beginning, not the complete file.
After reading and analyzing a 1MB file, if needed, should be read complete file and make changes to the file and re-save the file to archive.
Memory and speed of the program is very important. I used to set the buffer size of the function SetLength, unfortunately complete content of file files are stored in the buffer.
What do you think?
Use the overloaded version of TZipFile.Read() that returns a TStream instead of a TBytes. That way, you do not have to read the entire file into memory, and can read just its beginning bytes as needed.
Unfortunately, there is no way to modify data inside of a zip archive using TZipFile. Although you can Extract() a particular file, modify it externally as needed, and then Add() it back into TZipFile, there is no way to remove/replace a given file in TZipFile. TZipFile is a simple framework, it can only read a zip archive and add new files to it, nothing else. If you need more control over a zip archive, you are better off using a more complete third-party solution, such as ZipForge.

Delphi HtmlHelpAPI- How to direct a CHM file to open to different sections

I am able to open a CHM file by passing a ShortInteger and casting it as a Word for the dwData parameter. I.E.
Unit Help; //this is where the Id's are set with their description
Interface
Const
Address_File = 35; //delphi identifies Address_File as a shortint
etc..
Call get help pass my ID
GetHelp(Address_File); //call get help pass my ID to open to the Address_File topic
GetHelp procedure
procedure GetHelp(HelpID : Word);
begin
Application.HelpFile := ProgramPath + 'help.chm';
HtmlHelpW(0, PWideChar(Application.HelpFile),HH_HELP_CONTEXT , HelpID);
end;
HtmlHelpW function
function HtmlHelpW(hwndCaller : HWND; pszFile: PWideChar; uCommand : Integer;
dwData : DWORD) : HWND; stdcall; external 'hhctrl.ocx' name 'HtmlHelpW';
As I pass different ShortIntegers I am able to initialize the help file at different sections.
However I can't figure out how the values are mapped. There are some sections in the chm file that I want to be able to map to but the short Integer or context ID associated with them is not documented in the program or is not mapped.
Free Pascal comes with a chmls.exe util that has a command that tries to recover the alias (context) data:
chmls, a CHM utility. (c) 2010 Free Pascal core.
Usage: chmls [switches] [command] [command specific parameters]
Switches :
-h, --help : this screen
-p, --no-page : do not page list output
-n,--name-only : only show "name" column in list output
Where command is one of the following or if omitted, equal to LIST.
list <filename> [section number]
Shows contents of the archive's directory
extract <chm filename> <filename to extract> [saveasname]
Extracts file "filename to get" from archive "filename",
and, if specified, saves it to [saveasname]
extractall <chm filename> [directory]
Extracts all files from archive "filename" to directory
"directory"
unblockchm <filespec1> [filespec2] ..
Mass unblocks (XPsp2+) the relevant CHMs. Multiple files
and wildcards allowed
extractalias <chmfilename> [basefilename] [symbolprefix]
Extracts context info from file "chmfilename"
to a "basefilename".h and "basefilename".ali,
using symbols "symbolprefix"contextnr
extracttoc <chmfilename> [filename]
Extracts the toc (mainly to check binary TOC)
extractindex <chmfilename> [filename]
Extracts the index (mainly to check binary index)
This might be a start, since at least you'll know which pages are exported using an ID, and maybe the URL names will give some information.
The util is in recent releases (make sure you get 2.6.0) and also available in Free Pascal source, which should be convertable to Delphi with relatively minor effort.
Basically the chmls tool was created out of various test codebases. The testprograms decompiled and printed contents of different CHM sections and were used while creating the helpfile compiler, chmcmd, which is also part of FPC.
In Delphi, calling a help file is rather easy. In any VCL Forms application, you can set the HelpContext property of almost any control to a unique Context ID, which corresponds to a particular topic in the Help File. The Help File was compiled with these mappings, but when you decompile it, these mappings are no longer there. You must have access to the original help file project in order to know these ID's.
Set HelpContext of controls to the corresponding Context ID in the Help File
Set HelpType of controls to htContext to use the HelpContext ID
Assign Application.HelpFile to the appropriate location of the CHM file
When pressing F1 anywhere in your application, the help file will open based on the Help Context ID on the control, or its parent control
If you don't have the original project, and you don't want to re-create it, then you would have a long task of iterating through the Context ID's of your help file. Try to call the help file starting from 0 through 1,000 or possibly 50,000, depending on the size of it.
A practice I implement is a set of constants in a designated unit called HelpConstants.pas which is shared across our common application base. Each constant name uniquely and briefly describes the topic which it represents. Upon starting the application, I dynamically assign these Context ID's to their corresponding controls (usually forms) and VCL takes care of the rest.
I got the utility Marco suggested from
https://github.com/alrieckert/freepascal_arm/blob/master/packages/chm/bin/i386-win32/chmls.exe
(download by selecting View Raw).
I was able to extract all the context tags from the .chm help file and add the one I was interested in to my C++ Builder program by calling Application->HelpJump().
HTH

How do I use a custom AVI with TAnimate?

I have an AVI file that I pulled from shell32 using Resources Extract. I would like to use this with TAnimate but I can't figure out how to load this file.
I succesfully loaded the AVI into a .RES file using DelphiDabbler's rcdatacreator program (you have to download the "worked example" to get rcdatacreator. However, my issue now is figuring out how to extract the AVI file from the .RES and supplying it to TAnimate.
I am using Delphi 2010:
Any help is appreciated.
As Andreas mentioned (in his now deleted answer), you don't need to use an external tool to add the resource in recent versions of Delphi.
Use Project/Resources and Images... from the IDE menu. Add a new resource by browsing to the folder your .AVI file is in, give it a name, and type in AVI for the Resource Type. (It's not in the list, but you can add it.)
At runtime, use the following code:
// I used CoolAVI as the resource name in my image above,
// so that's the name I need to use here.
Animate1.ResName := 'COOLAVI';
Animate1.Active := True;

Loading the output from TOleContainer.SaveAsDocument

I have an existing database with blobs contain OLE compound files. I have a requirement to read these OLE compound files and open them in the Delphi 7 TOleContainer control.
Note that I don't have the source of the app that reads and write to the database. The database remains in active use, so my solution will be used on an ongoing basis, not just for a one-off data extraction.
TOleContainer has a SaveAsDocument method, and by experimentation I have found that, for a given file, this method produces OLE compound files which are identical to those created in the database when that file is added.
However, TOleContainer does NOT have a corresponding LoadFromDocument method. It has other Load* and Create* methods, but none seem capable or suitable for loading the output from SaveAsDocument.
The delphi 7 implementation of SaveAsDocument is this, from the OleCtnrs.pas module:
procedure TOleContainer.SaveAsDocument(const FileName: string);
var
TempStorage: IStorage;
PersistStorage: IPersistStorage;
begin
CheckObject;
if FModSinceSave then SaveObject;
FOleObject.QueryInterface(IPersistStorage, PersistStorage);
if PersistStorage <> nil then
begin
OleCheck(StgCreateDocFile(PWideChar(WideString(Filename)), STGM_READWRITE
or STGM_SHARE_EXCLUSIVE or STGM_CREATE, 0, TempStorage));
OleCheck(OleSave(PersistStorage, TempStorage, False));
PersistStorage.SaveCompleted(nil);
end;
end;
Please provide an implementation of LoadFromDocument which is capable of loading the output from SaveToDocument, and which I can use to patch OleCtnrs.pas. Or else point me to an existing solution.
Thanks!
You have to load the file by using TOleContainer.CreateObjectFromFile. Do not use TOleContainer.LoadFromStream/File, that only works with files that are saved with TOleContainer.SaveToStream/File. Files saved that way get a Delphi specific header containing a four byte code (BDOC) and size (and maybe something more).
According to the documentation for Delphi 2007 (should be the same for ), you can use 'TOleContainer.LoadFromStream'. From the Delphi 7 help file (emphasis mine):
Call LoadFromStream to load an OLE object from a stream. If OldStreamFormat is true, LoadFromStream loads OLE objects saved by a TOleContainer object as well as OLE objects saved using the current format; if OldStreamFormat is false, LoadFromStream will not load OLE objects saved by the library. If there's already an OLE object in the container, it is destroyed and any changes the user made to it are discarded.

Opening File paths with spaces in Delphi 5

(Using Delphi 5)
I am attempting to open a log file using the following code:
// The result of this is:
// C:\Program Files\MyProgram\whatever\..\Blah\logs\mylog.log
fileName := ExtractFilePath(Application.ExeName) + '..\Blah\logs\mylog.log';
// The file exists check passes
if (FileExists(fileName)) then
begin
logs := TStringList.Create();
// An exception is thrown here: 'unable to open file'
logs.LoadFromFile(fileName);
end;
If I relocate the log file to C:\mylog.log the code works perfectly. I'm thinking that the spaces in the file path are messing things up. Does anyone know if this is normal behavior for Delphi 5? If it is, is there a function to escape the space or transform the path into a windows 8.3 path?
I'm pretty sure that Delphi 5 handles spaces in filenames ok but it has been a very long time since I have used that specific version. Is the file currently open by another process? It also could be a permissions issue. Can you instead of loading it into a tStringList, try opening it with a tFileStream with the filemode set to "fmOpenRead or fmShareDenyNone".
fStm := tFileStream.Create( filename, fmOpenRead or fmShareDenyNone );
then load your tStringlist from the stream:
Logs.LoadFromStream ( fStm );
Are you sure its not the "..\" thats causing the problem rather than the spaces. Have you tried to see if it works at
c:\My\Path\nospaces\
If so and you are always using the ..\ path, maybe write a simple function to remove the last folder from your application path and create a full correct pathname.
It's odd that Delphi 5 would throw errors about this. I know of an issue with FileExists failing on files with an invalid last-modified-date (since it internally uses FileAge), but it's the opposite here. Instead of using "..\" I would consider risking the current path, and loading from a relative path: LoadFromFile('..\Something\Something.log'); especially for smaller applications, or by calling ExtractFilePath twice: ExtractFilePath(ExtractFilePath(Application.ExeName))
I'm pretty sure Delphi has always handled spaces so I doubt that is the issue.
You don't show the full path. Any chance it is really long? For example I could believe an issue with paths longer than 255 characters.
It's also a bad idea to put log files under Program Files. Often normal users are not given permission to write to anything under Program Files.
Delphi 5 can open files with spaces - that is certainly not the problem. To prove it, try copying it to c:\my log.log- it should open fine.
Is there any more information in the error message you receive? The most likely thing is that someone else (perhaps your own program) is still writing to the log.
The spaces are not a problem. While the '..' could be a problem in Delphi 5, mosts probably the file is locked by the process that writes to it. If you have control of it, make sure it opens the file with fmShareDenyWrite and not fmShareExclusive or fmShareCompat (which is the default).
Also, you can use:
fileName := ExpandFileName(ExtractFilePath(Application.ExeName) + '..\Blah\logs\mylog.log');
to obtain the absolute path from a relative path.
Also, as others have said, it is not good idea to write anything in Program Files. Regular users (that are not Administrators or Power Users) do not have rights to write there (although in Vista is will be virtualized, is is still not a good idea). Use the appropriate Application Data folder for the user (or all users). This folder can be obtained using:
SHGetFolderPath(0,folder,0,SHGFP_TYPE_CURRENT,#path[0])
where folder is either CSIDL_COMMON_APPDATA or CSIDL_LOCAL_APPDATA. See this delphi.about.com article for an example.
Simple :
// if log file = "C:\Program files\mylog.log"
// you'll get :
// »»»»» fileName = 'C:\Program files..\Blah\logs\mylog.log'
// if log file = "C:\mylog.log"
// you'll get :
// »»»»» fileName = 'C:..\Blah\logs\mylog.log'
Try this code instead, I'm pretty sure it will fit your needs :
fileName := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName))
+ '..\Blah\logs\mylog.log';
Regards,
Olivier
Delphi 5 has never had a problem opening files with spaces and I am still using it since it is uber stable and works great for older XP apps. You need to check your code closely.

Resources