Use SSL with Delphi yet still having a single exe - delphi

We use Indy and we need SSL eMail support in our app., however we need to have our application in a single .Exe.
We know that the default Indy handler requires to have the dlls in the path. Extracting the Dlls from one of the EXE's resources would be the last resort.
Any better ideas?

Try SSLBlackBox.

TOndrey gave you a good answer. I use SecureBlackBox as well.
You may consider some other third party components:
StreamSec
SecureBridge from DevArt

Be aware: if you add SSL/TLS support inside your executable, it might become restricted for export. If you're in the USA, this could mean that your application cannot be sold or given to people outside the USA. This is why these DLL's aren't part of Indy or Delphi themselves.
The libraries that Delphi uses are actually compiled DLL's from the OpenSSL project. But if you have a good knowledge of C then you should be able to compile the source to .obj files and link them with your Delphi code instead. You would probably need to modify part of the Indy code for this too. Of course, others could have done this too, but that makes the export of those Indy components (or even Delphi itself) more complex, because of those export restrictions.
Funnily enough, source code is protected by the first amendment which basically allows you to print the code in a book and then send it to some rogue nation. While if you'd send it in digital form (compiled or not) then you're committing a federal offence and probably will have to be careful when picking up the soap in the shower for at least a year... No one claimed that laws make sense. They can just be a pain in the [beep]...
Other SSL solutions don't work together with the Indy components, which would mean you'd have to rewrite part of your code to support those other solutions.
This link tells how you can load a DLL from memory, so you don't need to have it on disk. It's an alternate solution which I haven't tried. I don't think it will work, since the two DLL's depend on each other, but it might be worth a try...

Is the "Single EXE" requirement for distribution purposes or must it also be a single .EXE file when running on the client's machine?
If it's only for distribution purposes, you can append the DLL files to the end of your .EXE file and then - when the program starts - extract them from the .EXE file and store them locally as .DLL files, something like this:
VAR F,O : FILE;
VAR BUF : ARRAY[1..<MaxSizeOfDLLs>] OF BYTE;
ASSIGN(F,ParamStr(0)); RESET(F,1);
SEEK(F,<OriginalExeSize>);
BLOCKREAD(F,BUF,<FirstDllSize>);
ASSIGN(O,<NameOfFirstDLL>); REWRITE(O,1);
BLOCKWRITE(O,BUF,<FirstDllSize>); CLOSE(O);
BLOCKREAD(F,BUF,<SecondDllSize>);
ASSIGN(O,<NameOfSecondDLL>); REWRITE(O,1);
BLOCKWRITE(O,BUF,<SecondDllSize>); CLOSE(O);
SEEK(F,<OriginalExeSize>); TRUNCATE(F); CLOSE(F)
Quick'n'Dirty, not properly formatted, etc., but should give you the basic idea.

Have you tried compiling the OpenSLL source yourself and importing the object files into Delphi?
Recommended reading: Using C object files in Delphi - explains how to create a program that does not need a DLL, and can be deployed in one piece

I use Microsoft's CAPICOM for SSl3 and it solved my needs... It's freely redistributable but discontinued
If you try other components maybe you should look to SYNAPSE(at http://synapse.ararat.cz/) (I also use) it can work with StreamSec(and others) to send emails over ssl. Its free and easy to work.

Const
cdoSendUsingMethod = 'http://schemas.microsoft.com/cdo/configuration/sendusing';
cdoSMTPServer = 'http://schemas.microsoft.com/cdo/configuration/smtpserver';
cdoSMTPServerPort = 'http://schemas.microsoft.com/cdo/configuration/smtpserverport';
cdoSendServerPort = '25';
cdoSendUsingPort = 2;
cdoSMTPConnectionTimeout = 'http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout';
cdoSMTPAuthenticate = 'http://schemas.microsoft.com/cdo/configuration/smtpauthenticate';
cdoAnonymous = '0';
cdoBasic = '1';
cdoSMTPUseSSL = 'http://schemas.microsoft.com/cdo/configuration/smtpusessl';
cdoSendUserName = 'http://schemas.microsoft.com/cdo/configuration/sendusername';
cdoSendPassword = 'http://schemas.microsoft.com/cdo/configuration/sendpassword';
cdoURLGetLatestVersion = 'http://schemas.microsoft.com/cdo/configuration/urlgetlatestversion';
...
function SensCDOMail (ASubject, AFrom, ATo, ABody, ASmtpServer : WideString): String;
var
cdoMessage:OleVariant;
cdoConfiguration: OleVariant;
begin
//Configuration Object
cdoMessage:= CreateOleObject('CDO.Message');
cdoConfiguration:= CreateOleObject('CDO.Configuration');
try
cdoConfiguration.Fields(cdoSendUsingMethod):= cdoSendUsingPort;
cdoConfiguration.Fields(cdoSMTPServer):= ASmtpServer;
cdoConfiguration.Fields(cdoSMTPServerPort):= cdoSendServerPort;
cdoConfiguration.Fields(cdoSMTPAuthenticate):= cdoAnonymous;
cdoConfiguration.Fields(cdoSMTPUseSSL ):= True; // use SSL
cdoConfiguration.Fields.Update;
cdoMessage.Configuration:= cdoConfiguration;
cdoMessage.To := ATo;
cdoMessage.From := AFrom;
cdoMessage.Subject := ASubject;
//cdoMessage.HTMLBody := ABody; //Want to send in Html format
cdoMessage.TextBody := ABody; //Want to send in text format
cdoMessage.Send;
finally
VarClear(cdoMessage);
VarClear(cdoConfiguration);
end;
end;

It is possible to include these DLLs into the program's executable as resources and either export them to files when used or even use them without exporting them first by relocating the code and searching the entry points in memory. I have got code somewhere for doing the latter....

Related

How do I work with Word Documents without using COM Automation?

I have read multiple posts on the issue, and none seem to come to a decent conclusion to my question. (Perhaps I'm trying to see if anything has popped up lately.)
I have a small charity app that handles pledges. In doing so, it needs to work with and print documents.
Thing is, if Word is open in the background, the app thread will hang and won't respond to the closure of Word, and I have to roll back manually and close word. Sure, that all works fine, but I simply cannot guarantee that the end user will close Word, even if I put the instruction in a user manual.
I'm not too fussed about speed, but I guess that if it can be enhanced, it would be a nice little bonus.
Have any libraries been released for Delphi that will allow me to open, edit, print, and save documents? If not, is there a way to use Word Automation in such a way that it will not conflict with another open handle of Word when opened?
If you use GetActiveOleObject, you will get the running instance of Word.
By using CreateOleObject, you will get a new instance and shouldn't be troubled by other running instances.
In case you use the TWordApplication, wrapper you can set ConnectKind to ckNewInstance to accomplish this. By default, TWordApplication will try to connect with a running instance.
If you want to open edit and print Word documents and you don't mind using RTF format for what you're doing, investigate TRichView.
It will generate rich documents that are in RTF format, which is one of the formats MS word supports. I don't think it directly reads .DOC files but you can convert .DOC and .DOCX into RTF, for most simple files, but certain advanced formatting features would be lost in the conversion.
It has the advantage of working without any need for even any copy of MS Word to be installed on the machine that is going to do the document processing. For production of receipts and other simple documents, this would be the most reliable technique; Don't use Word directly, at all.
procedure PrintViaWord (const filename: string);
const
wdUserTemplatesPath = 2;
var
wrdApp, wrdDoc, wrdSel: variant;
begin
wrdApp:= CreateOleObject ('Word.Application'); // create new instance
sleep (5000); // this fixes a bug in Word 2010 to do with opening templates
wrdDoc:= wrdApp.documents.add (
wrdApp.Options.DefaultFilePath[wdUserTemplatesPath] + '\mytemplate.dot');
wrdDoc.Select;
wrdSel:= wrdApp.selection;
wrdApp.Options.CheckSpellingAsYouType:= 0;
wrdSel.paragraphformat.alignment:= 1;
wrdSel.typetext ('This is a program demonstrating how to open Word in the background'
+ ' and add some text, print it, save it and exit Word');
wrdDoc.SaveAs (filename + '.doc');
wrdApp.ActivePrinter:= 'Konica Minolta 211';
wrdApp.PrintOut;
WrdDoc.SaveAs (filename + '.doc');
wrdApp.quit;
wrdSel:= unassigned;
wrdDoc:= unassigned;
wrdApp:= unassigned
end;

(How) Can I use FutureWindows with standard file open dialogs?

I've been trying to use tomazy's FutureWindows infrastructure (see his answer at Delphi GUI Testing and Modal Forms or the home of the tool at https://github.com/tomazy/DelphiUtils), but would like to know if and how can it be used with standard Windows file open dialogs? They don't seem to be inheriting from TControl, which the FutureWindows infra seems to assume (unless I've misunderstood it).
What I'd like to do is basically to just select a file in an OpenFileDialog which is opened modally by a command within my testing, but haven't yet been able to figure out how to do this.
Use a tool like Spy++ to find out what the window class name is. For example, on my Windows 7 machine, the window class name for a system file open dialog is #32770 (Dialog).
My current solution is below:
TFutureWindows.Expect(MESSAGE_BOX_WINDOW_CLASS)
.ExecProc(
procedure (const AWindow: IWindow)
var
DlgHandle: HWND;
FileName: string;
begin
FileName := ExpandFileName('myFileToUse.txt');
DlgHandle := AWindow.GetHandle;
Windows.SetDlgItemText(DlgHandle, 1148, PChar(FileName));
end
)
.ExecSendKey(VK_RETURN);
So basically sending a message using Windows API. The ideas (and the ID 1148) were found from here: http://social.msdn.microsoft.com/forums/en-US/winforms/thread/62d5db14-5497-4ceb-8af0-d7f81732e937/
Possible better solutions are welcome, but this seems fine enough for me at least for now.
Thanks for the comments so far!

any functions to create zip file of directory/file on vista with delphi 2009

I am looking for a simple method of zipping and compressing with delphi. I have already looked at the components at torry delphi:http://www.torry.net/pages.php?s=99. They all seem as though they would accomplish what I want however a few disadvantages to using them is that none of them run in delphi 2009 and are very complex which makes it difficult for me to port them to delphi 2009. And besides, the documentation on them is scarce, well at least to me. I need basic zipping functionality without the overhead of using a bunch of DLLs. My quest lead me to FSCTL_SET_COMPRESSION which I thought would have settled the issue but unfortunately this too did not work. CREATEFILE looked promising, until I tried it yielded the same result as FSCTL_SET... I know that there are some limited native zipping capability on windows. For instance if one right clicks a file or folder and selects -> sendTo ->zipped folder, a zipped archive is smartly created. I think if I was able to access that capability from delphi it will be a solution. On a side issue, does linux have its own native zipping functions that can be used similar to this?
TurboPower's excellent Abbrevia can be downloaded for D2009 here, D2010 support is underway and already available in svn according to their forum.
Abbrevia used to be a commercial (for $$$) product, which means that the documentation is quite complete.
I use Zipforge. Why are there problems porting these to D2009? Is it because of the 64bit??
Here is some sample code
procedure ZipIt;
var
Archiver: TZipForge;
FileName: String;
begin
try
Archiver:= TZipForge.create(self);
with Archiver do begin
FileName := 'c:\temp\myzip.zip';
// Create a new archive file
OpenArchive(fmCreate);
// Set path to folder with some text files to BaseDir
BaseDir := 'c:\temp\';
// Add all files and directories from 'C:\SOURCE_FOLDER' to the archive
AddFiles('myfiletozip.txt');
// Close the archive
CloseArchive;
end;
finally
Archiver.Free;
end;
end;
If you can "do" COM from Delphi, then you can take advantage of the built-in zip capability of the Windows shell. It gives you good basic capability.
In VBScript it looks like this:
Sub CreateZip(pathToZipFile, dirToZip)
WScript.Echo "Creating zip (" & pathToZipFile & ") from folder (" & dirToZip & ")"
Dim fso
Set fso= Wscript.CreateObject("Scripting.FileSystemObject")
If fso.FileExists(pathToZipFile) Then
WScript.Echo "That zip file already exists - deleting it."
fso.DeleteFile pathToZipFile
End If
If Not fso.FolderExists(dirToZip) Then
WScript.Echo "The directory to zip does not exist."
Exit Sub
End If
NewZip pathToZipFile
dim sa
set sa = CreateObject("Shell.Application")
Dim zip
Set zip = sa.NameSpace(pathToZipFile)
WScript.Echo "opening dir (" & dirToZip & ")"
Dim d
Set d = sa.NameSpace(dirToZip)
For Each s In d.items
WScript.Echo s
Next
' http://msdn.microsoft.com/en-us/library/bb787866(VS.85).aspx
' ===============================================================
' 4 = do not display a progress box
' 16 = Respond with "Yes to All" for any dialog box that is displayed.
' 128 = Perform the operation on files only if a wildcard file name (*.*) is specified.
' 256 = Display a progress dialog box but do not show the file names.
' 2048 = Version 4.71. Do not copy the security attributes of the file.
' 4096 = Only operate in the local directory. Don't operate recursively into subdirectories.
WScript.Echo "copying files..."
zip.CopyHere d.items, 4
' wait until finished
sLoop = 0
Do Until d.Items.Count <= zip.Items.Count
Wscript.Sleep(1000)
Loop
End Sub
COM also allws you to use DotNetZip, which is a free download, that does password-encrypted zips, zip64, Self-extracting archives, unicode, spanned zips, and other things.
Personally I use VCL Zip which runs with D2009 and D2010 perfectly fine. it does cost $120 at the time of this post but is very simple, flexible and most of all FAST.
Have a look at VCLZIP and download the trail if your interested
code wise:
VCLZip1.ZipName := ‘myfiles.zip’;
VCLZip1.FilesList.add(‘c:\mydirectory\*.*’);
VCLZip1.Zip;
is all you need for a basic zip, you can of course set compression levels, directory structures, zip streams, unzip streams and much more.
Hope this is of some assistance.
RE
Take a look at this OpenSource SynZip unit. It's even faster for decompression than the default unit shipped with Delphi, and it will generate a smaller exe (crc tables are created at startup).
No external dll is needed. Works from Delphi 6 up to XE. No problem with Unicode version of Delphi. All in a single unit.
I just made some changes to handle Unicode file names inside Zip content, not only Win-Ansi charset but any Unicode chars. Feedback is welcome.

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.

Delphi: EReadError with message 'Property Persistence does Not Exist'

My program written with Delphi 7 compiles OK, but when I run it it gives me the error message:
Project1.Exe raised exception class EReadError with Message 'Property Persistence does Not Exist'. Process Stopped.
This only started after I installed the TMS Component Pack to use with this project. Thanks for any help.
Open the Form in Delphi IDE
Use Alt + F12 to edit the .DFM source
Search the "Persistence" property
Delete the line with "Persistence" property
DFM example:
SomeComponent1 = TSomeComponent
OtherProperty = OtherValue
Persistence = True
AnotherProperty = AnotherValue
end
Also you can use the great DFMCheck 1.4 tool, by Andreas Hausladen. To check any other missing property like that:
http://andy.jgknet.de/blog/?page_id=177
This is most likely caused by the compiled & installed package being out of sync with the actual .pas file. If you have source code then rebuilding the packages will probably fix it.
Set a breakpoint(F5) and step the program(F7/F8).Get to the location where you get that exception and then give us more information about it(show some code).
This error means that it's trying to load something (usually a form) from a DFM resource and it comes up with a value for a property that the component it's creating doesn't have.
If it only happened when you started using TMS components, the solution is simple: don't use them. Send as much information as you can about the error and the project that caused it to the authors and see if they can find a way to fix it. Until then, use something else.
If you're using text DFMs (right click on the form, check "Text DFM", save), you can use Search|Find in Files to find all instances of Persistence in your DFM files. Just set the search string to "Persistence" (I usually tell it to ignore case), the file mask to "*.dfm", and check the "All files in project" checkbox.
If you're not already using text DFMs and don't want to manually open all forms and check the box and then resave them, you can use CONVERT.EXE (in the ($DELPHI)\Bin folder) to convert them en-masse. Run CONVERT with no parameters from any command prompt to see the options. By default, CONVERT will save .DFM as .txt, but you can have it work in-place (rewriting the binary .DFM as the text .DFM) by using the -i switch. (I usually back up the .DFMs to a different folder first, then convert them. If no errors are reported, I can then delete the backed up .DFMs later.)
I had similar problem with TMS when I upgraded to a new version:
If you think that some particular component is causing the problem,
delete it , compile project without it, place it on the form/frame again.
If that doesn't work for you:
Things you need to do in order to fix the problem , so you can use Designer and new properties, because that's what you really want , don't you ? :-) :
Uninstall TMS Component Pack ( or whatever you're using )
Re-Install TMS Component Pack
Build & Install the packages
Add appropriate TMS .lib files to your Application Project ( I'm using C++ Builder )
Add appropriate TMS .pas files to your Application Project . For example I had a problem with TAdvSmoothCalendar component , so I've added the AdvSmoothCalender.pas to my project.
Cheers!
I hope it works for everyone with a similar problem :)
I had similar problem with nuiGui Delphi Framework,
To Solve this, create a include file with some properties and use it in your class.
/// include class 'Basic.inc'
private
function GetWidth: Integer;
published
property ClientHeight : Integer Read FHeight Write FHeight;
//property ClientWidth : Integer Read FWidth Write FWidth;
//property OldCreateOrder : Boolean Read FOldCreateOrder Write FOldCreateOrder;
end;
...
/// main class like this
TuMemoFrame = class(TUniFrame)
UniMemo1: TUniMemo;
UniMemo2: TUniMemo;
UniButton1: TUniButton;
procedure UniButton1Click(Sender: TObject);
private
public
{$Include Basic.inc } // <---
end;

Resources