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

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!

Related

Delphi - How to Register File extension for auto-open? [duplicate]

This question already has answers here:
How to associate a program with a file type, but only for the current user?
(2 answers)
Closed 8 years ago.
How can i auto-open a custom extension in my Delphi app ? I'm trying to make application
that loads text files but with a different extension.
Example : Text.DZ | all i want is when i click the file with .DZ Extension it opens my app automatically and loads the text inside into the memo1.lines.text.
I know that i have to register my new custom extension but i have no idea how to do it and
load the text into my app.
Also if you can include a source-code example that would be awesome.
Any help will be much appreciated ! and
Sorry for my newbiness and bad English explanation but i hope you guys understand me (^-^)/.
There's a way to do it programmatically, although I don't recall how off the cuff.
That said, if you're just wanting to do it for your own personal use, not for a piece of software you'll be distributing to others, then the easiest way is to use Windows Explorer: click the data file you want, then right-click and select Open With -> ... you'll probably need to select the Other... option at the bottom. That will bring up a dialog box that lets you choose your app. There should also be a checkbox somewhere that says something like "Make Default" or "Always Open With This" or something along those lines. Make sure that box is checked. Then click OK a couple of times and you're off to the races.
But you'll also need to set up your app to read the filename from the command line. You use ParamStr and ParamCount for this.
for i := 0 to ParamCount do
ShowMessage('Parameter '+IntToStr(i)+' = '+ParamStr(i));
When you double-click on the data file, it will open your app and pass this filename as ParamStr(1). So when you get it, simply do something like this:
memo1.lines.LoadFromFile( ParamStr(1) );
Use this as a STARTING POINT (it won't compile if you just copy-and-paste!):
procedure TMyForm234:FormCreate( blah blah )
begin
if ParamCount > 0 then
theMemo.Lines.LoadFromFile( ParamStr(1) );
end;

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;

Use SSL with Delphi yet still having a single exe

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....

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