I am currently using Delphi 7 on XP, but I would like to eventually migrate the code to DXE on Win8.
I am trying to send email using the JCL, using JCLMAPI to be specific. I tried using the JclSimpleSendMail routine in the JCLMAPI unit. Here's the interface to the call.
function JclSimpleSendMail(const Recipient, Name, Subject, Body: AnsiString; const Attachment: TFileName; ShowDialog: Boolean; ParentWND: THandle; const ProfileName: AnsiString; const Password: AnsiString): Boolean;
The problem is it pops up the default MAPI client message box modally (in my case Outlook 2010). I would like it to just open the email message window, but allow the user to continue working in the Delphi App. until they are ready to send, eg, in case a user wants to continue working in the Delphi App before sending the email. Is this possible?
I noticed there is a ParentHWND property in TJCLEmail, I tried setting that to zero (I know it was a reach), but I was hoping that removing the parent handle might change the modal behavior (no luck!)
function TForm1.SimpleSendHelper2(const ARecipient, AName, ASubject, ABody: AnsiString; const AAttachment: TFileName;
AShowDialog: Boolean; AParentWND: THandle; const AProfileName, APassword, AAddressType: AnsiString): Boolean;
var
AJclEmail: TJclEmail;
begin
AJclEmail := TJclEmail.Create;
try
**AJCLEmail.ParentWnd := 0; //TRIED FORCING THE ATTACHED HANDLE TO ZERO**
*//if AParentWND <> 0 then
// AJclEmail.ParentWnd := AParentWND;*
if ARecipient <> '' then
AJclEmail.Recipients.Add(ARecipient, AName, rkTO, AAddressType);
AJclEmail.Subject := ASubject;
AJclEmail.Body := ABody;
if AAttachment <> '' then
AJclEmail.Attachments.Add(AnsiString(AAttachment));
if AProfileName <> '' then
AJclEmail.LogOn(AProfileName, APassword);
Result := AJclEmail.Send(AShowDialog);
finally
AJclEmail.Free;
end;
end;
This also successfully opened up the Default MAPI app and filled in all of the information passed (TO, Subject, Body, Attachment). Unfortunately it still opens the message box modally.
Finally, I also tried the code at http://www.delphifaq.com/faq/delphi/network/f236.shtml This code just Uses MAPI directly (no JCL). Unfortunately, it also pops up the message box modally.
Any thoughts on how I can open the default MAPI client non-modally?
Thank you!
You can use Windows API function MAPISendMailW with flag MAPI_DIALOG_MODELESS assigned.
But then you have to use MAPISendMailHelper function for Win8 and later and MAPISendMailW for Windows 7 and earlier. And for Windows 7 such functionality available only with some (latest) versions of Office and only with Windows SDK for Windows 8 installed (according to MSDN). If another email client used (not MS Outlook), then there is no guarantee to get it working.
In other words, it is possible, but it is tricky. I suggest you keep it in modal form, it is safer for many reasons. If user "is not ready to send email", then he will not activate such function (or cancel it to return to the program).
Related
In many confirmation dialogs it is usefull to have such option (quick wayt to disable confirmation).
But i can't find how to do that. I don't want to design it myself because i need this dialog to be standard-like and don't wont to redesign with every update of Delphi. Is there simple way to use Delphi standard confirmation dialog with such checkbox ?
UPDATE2. Suggested SynTaskDialog library from Synopse project does great job (all i need and even more), i will use it in my projects. Thanks!
UPDATE. So, thank you guys for ideas. System function MessageBoxCheck is nice solution but seem to be not so stable as it should be. In general i agree that it is good idea to use latest API functions to provide users with best UI experience of modern os and use old-fashioned design for older systems. At moment i stay on simple solution (code is following), but if someone share the code with support of UI for modern OS, it will be nice.
function MsgDlgWithCB(const Msg,Title,CBMsg: string; DlgType: TMsgDlgType;
Buttons: TMsgDlgButtons; DefaultButton: TMsgDlgBtn;
var cbDontAskAnymore: TCheckBox): TForm;
var
i: integer;
b: TButton;
y: integer;
begin
Result := CreateMessageDialog(Msg, DlgType, Buttons, DefaultButton) ;
Result.Position := poScreenCenter;
cbDontAskAnymore := TCheckBox.Create(Result);
cbDontAskAnymore.Caption := CBMsg;
cbDontAskAnymore.Width := 130;
y := -1;
for i := 0 to result.ComponentCount-1 do
if result.Components[i] is TButton then
begin
b := TButton(result.Components[i]);
b.Left := b.Left + cbDontAskAnymore.Width + 16;
Result.ClientWidth := Max(Result.ClientWidth, b.Left+b.Width+16);
y := b.Top+b.Height-cbDontAskAnymore.Height;
end;
if y<0 then
y := Result.ClientHeight - cbDontAskAnymore.height - 16;
Result.Caption := Title;
cbDontAskAnymore.Parent := Result;
cbDontAskAnymore.Top := y;
cbDontAskAnymore.Left := 8;
end;
function MessageDlgCheckbox(const Msg: string; DlgType: TMsgDlgType;
Buttons: TMsgDlgButtons; DefaultButton: TMsgDlgBtn;
var cbDontAskAnymore: Boolean;
const Title: string ='Confirmation';
const CBMsg: string = 'Don''t ask anymore'): integer;
var
f: TForm;
c: TCheckbox;
begin
f := MsgDlgWithCB(Msg,Title,CBMsg,DlgType,Buttons,DefaultButton,c);
try
result := f.ShowModal;
cbDontAskAnymore := c.Checked;
finally
f.free;
end;
end;
You can use our Open Source SynTaskDialog unit.
Windows provides a generic task dialog available since Vista/Seven. But there is none available with previous versions of Windows, i.e. Windows XP or 2K.
This unit (licensed under a MPL/GPL/LGPL tri-license) will use the new TaskDialog API under Vista/Seven, and emulate it with pure Delphi code and standard themed VCL components under XP or 2K. It supports Delphi 6 up to XE4, and is Win32/Win64 Unicode ready.
Here is the result under a Windows Seven 64 bit computer:
And here is the same dialog created from our emulated pure Delphi code:
Since this screenshot was made on a Win 7 machine, the styling is native for that OS. When the emulated version of the dialog runs on XP it displays in a style native to that OS.
You have your "Do not ask for this setting next time" checkbox... and potentially much more!
The system native functionality that offers such facilities is the task dialog API introduced in Vista. This provides means for you to show much more capable dialogs than the older MessageBox API.
Should you need to support XP then you will have to create your own dialog. For example by deriving from TForm and calling ShowModal. If you do this, make the form capable of building itself dynamically. Don't make one form per message that you show!
In my codebase, I have my own wrapper of the task dialog API. This detects at runtime versions of Windows that do not support task dialog and falls back on a custom built Delphi dialog.
Regarding SHMessageBoxCheck I'd be a little wary of taking a dependency on that. According to its documentation it's not supported beyond XP, and you have to import it by ordinal. I'd personally be worried that it might be dropped from a future version of Windows. That said, MS has a strong track record of doing whatever it takes to keep legacy apps working with new OS releases.
I need to send several virtual keys (VK_RETURN) from my delphi application (myapp.exe) into another application (target.exe).
Eg : Send VK_RETURN twice , from myapp.exe , into target.exe
The OS that I use are Windows 7 64 bit and Windows XP.
I read : How to send an "ENTER" key press to another application? , Send Ctrl+Key to a 3rd Party Application (did not work for me) and other previous asked question.
But still I'm getting confused.
How to set the focus to the target application ?
How to send the virtual keys to the targeted application ?
Simple example : I want to send VK_RETURN twice into notepad.exe or calc.exe (already loaded) or any other program from my delphi application. How to do that ?
The simplest way to do this in Delphi 2010, please...
PS :
I tried SndKey32.pass from http://delphi.about.com/od/adptips2004/a/bltip1104_3.htm
And got error : [DCC Error] SndKey32.pas(420): E2010 Incompatible types: 'Char' and 'AnsiChar'
If (Length(KeyString)=1) then MKey:=vkKeyScan(KeyString[1])
If your target application isn't the foreground window, you need to use PostMessage to send keystrokes to its window handle. You can get that window handle using FindWindow. The code below sends the Enter key to a the text area in a running instance of Notepad (note it uses an additional FindWindowEx to locate the memo area first). It was tested using both Delphi 2007 and Delphi XE4 (32-bit target) on Windows 7 64.
uses Windows;
procedure TForm1.Button1Click(Sender: TObject);
var
NpWnd, NpEdit: HWnd;
begin
NpWnd := FindWindow('Notepad', nil);
if NpWnd <> 0 then
begin
NpEdit := FindWindowEx(NpWnd, 0, 'Edit', nil);
if NpEdit <> 0 then
begin
PostMessage(NpEdit, WM_KEYDOWN, VK_RETURN, 0);
PostMessage(NpEdit, WM_KEYUP, VK_RETURN, 0);
end;
end;
end;
To find the window by title (caption) instead, you can just use the second parameter to FindWindow. This finds a new instance of Notepad with the default 'Untitled' file open:
NpWnd := FindWindow(nil, 'Untitled - Notepad');
Note that this requires as exact match on the window title. An extra space before or after the -, for instance, will cause the match to fail and the window handle to not be retrieved.
You can use both the window class and title if you have multiple instances running. To find the copy of Notepad running with Readme.txt loaded, you would use
NpWnd := FindWindow('Notepad', 'Readme.txt - Notepad');
To find other applications, you'll need to use something like WinSpy or WinSight to find the window class names. (There are others also, such as Winspector or WinDowse (both of which are written in Delphi).)
Your comment mentions Calculator; according to Winspector, the Calculator main window is in a window class called CalcFrame on Windows 7, and the area the numbers are displayed in is a Static window (meaning it doesn't seem to receive keystrokes directly). The buttons are simply called Button, so you'd have to loop through them using EnumChildWindows looking for the individual buttons to identify them in order to obtain their handles.
(How to enumerate child windows is a separate question; you can probably find an example by searching here or via Google. If you can't, post a new, separate question about that and we can try to get you an answer.)
Here's a quick example of sending keys to Calculator after finding it by window class. It doesn't do anything useful, because it needs some time spent to identify different buttons and the keys that each responds to (and the proper combination of messages). This code simply sends 11Numpad+22 to the calculator window (a quick test showed that they were properly received and displayed, and that's about all the time I wanted to spend on the process).
uses Windows;
procedure TForm1.Button1Click(Sender: TObject);
var
NpWnd: HWnd;
begin
NpWnd := FindWindow('CalcFrame', nil);
if NpWnd <> 0 then
begin
PostMessage(NpWnd, WM_KEYDOWN, VK_NUMPAD1, 0);
PostMessage(NpWnd, WM_KEYDOWN, VK_ADD, 0);
PostMessage(NpWnd, WM_KEYDOWN, VK_NUMPAD2, 0);
end;
end;
I have a Delphi XE app and I'd like to pop up the address book dialog that Outlook uses from within my Delphi app - I assume there are COM classes to support this? What's the best way to get this done? Platform is Win7-64 with Outlook 2010.
TIA
Disclaimer: It is definitely possible to do so through COM, but Outlook will display warnings that a 3rd party application is accessing the address book (and rightfully so). If you want to avoid these warnings, you can run the code from within an Outlook add-in, resort to MAPI, or use Outlook Redemption, which is basically an advanced wrapper around MAPI that feels like the Outlook Object Model.
The Outlook Object Model offers the SelectNamesDialog dialog to display the address book. It is highly configurable, and you can initialize it with custom sets of addresses as well.
As a little example, here is some code that pops up the address book in multiselect mode. For brevity, it uses late binding (OleVariants). You'll probably want to use early binding in production code.
procedure TForm1.Button1Click(Sender: TObject);
var
application: OleVariant;
dialog: OleVariant;
i: Integer;
recipients: String;
recipient: OleVariant;
begin
application := createOleObject( 'Outlook.Application' );
// Obtain the dialog
dialog := application.session.getSelectNamesDialog;
// Only show the a single 'add' field, multiselect
dialog.setDefaultDisplayMode( 6 ); // 6 = olDefaultDelegates
// Display the dialog
dialog.display;
// Display selection
recipients := '';
for i := 1 to dialog.recipients.count do
begin
recipient := dialog.recipients.item( i );
recipients := recipients + recipient.name + #13#10;
end;
showMessage( recipients );
end;
To do something like this you need to support the Extended MAPI interface.
Here is a link to a component which supports this on Win7-64 Outlook-2010.
Easy MAPI
Supports execution of address book dialogs.
I am trying to get access to the IWebBrowser2 object from Internet Explorer 8 with the chrome plugin. I am able to access it when the chrome plugin isn't installed, but it doesn't work due to the class names etc different.
Without chrome plugin I can use:
function GetIEFromHWND(WHandle: HWND; var IE: IWebbrowser2): HRESULT;
var
hInst: HWND;
lRes: Cardinal;
MSG: Integer;
pDoc: IHTMLDocument2;
ObjectFromLresult: TObjectFromLresult;
begin
Result := 0;
hInst := LoadLibrary('Oleacc.dll');
#ObjectFromLresult := GetProcAddress(hInst, 'ObjectFromLresult');
if #ObjectFromLresult <> nil then begin
try
MSG := RegisterWindowMessage('WM_HTML_GETOBJECT');
SendMessageTimeOut(WHandle, MSG, 0, 0, SMTO_ABORTIFHUNG, 1000, lRes);
Result := ObjectFromLresult(lRes, IHTMLDocument2, 0, pDoc);
if Result = S_OK then
(pDoc.parentWindow as IServiceProvider).QueryService(IWebbrowserApp, IWebbrowser2, IE);
finally
FreeLibrary(hInst);
end;
end;
end;
This doesn't work (I'm presuming) because there's no IHTMLDocument2 interface (using MS Spy++ you can see that the window heirarachy is completely different).
I can access the instance of the "Tab" that I'm after, but ultimately I need to "refresh" that tab with a new URL (which I was going to use IWebBrowser2.Navigate to accomplish).
I've tried importing the type library for Chrome but I can't find anything in there to help either. So I'm happy to utilise whatever I need to, in order to refresh tab that I have the handle to.
Thanks
If you need to use Chrome Frame I have wrapped the ActiveX control here:
http://www.progdigy.com/?p=116
But I would suggest you to use Delphi Chromium Embedded Instead, you will have more possibilities.
http://code.google.com/p/delphichromiumembedded/
This is approach I found for Tray ... :
http://www.programmersheaven.com/mb/delphikylix/257563/257563/how-can-i-make-a-system-tray-flash/
Does the same technique works for Dialogs ( as they are forms with addition params, in fact )?
Or I can do it with way faster methods like getting handle / address / interface and overload or overdrive the function with FlashWindow(Ex) method?
I mean - can I make, for example ShowMessage(), window / dialog flash using FlashWindowEx() method and if I can, can it be done using the example in link given above?
Please, point to best direction or clarify my doubts ..
Thanks.
Sorry for bad formulation of question.
The same technique applies to any top-level window, including dialog boxes. If you can get the window's handle, you can pass it to FlashWindowEx.
There are many ways a modal form or dialog (both VCL or native from the system) can be shown from a Delphi program, so you need to somehow hook into message processing and catch messages that are sent when a modal form or dialog is shown.
For that a message hook can be set using the SetWindowsHookEx() API function. Since you need this only while the application is inactive you could set it in the handler of the OnDeactivate application event, and reset it in the handler for the OnActivate application event:
var
gNextHook: HHOOK;
procedure TForm1.AppActivate(Sender: TObject);
begin
if gNextHook <> 0 then
UnhookWindowsHookEx(gNextHook);
gNextHook := 0;
end;
procedure TForm1.AppDeactivate(Sender: TObject);
begin
gNextHook := SetWindowsHookEx(WH_CALLWNDPROC, #WndProcHook, 0,
GetCurrentThreadId);
end;
The hook function would watch for messages that are sent when a modal dialog or form is shown, and call FlashWindowEx() with the correct parameters:
function WndProcHook(nCode: integer; AWParam: WPARAM; ALParam: LPARAM): LRESULT; stdcall;
var
DataPtr: PCWPStruct;
Fwi: TFlashWInfo;
begin
DataPtr := PCWPStruct(ALParam);
if (DataPtr^.message = WM_INITDIALOG)
or ((DataPtr^.message = CM_ACTIVATE) and (DataPtr^.lParam = 0) and (DataPtr^.wParam = 0))
then begin
Fwi.cbSize := SizeOf(TFlashWInfo);
// flash caption of new modal window
Fwi.hwnd := DataPtr^.hwnd;
Fwi.dwFlags := FLASHW_ALL or FLASHW_TIMERNOFG;
Fwi.uCount := 0;
Fwi.dwTimeout := 0;
FlashWindowEx(Fwi);
// uncomment this to flash task bar button as well
(*
Fwi.hwnd := Application.MainForm.Handle;
Fwi.dwFlags := FLASHW_TRAY or FLASHW_TIMERNOFG;
FlashWindowEx(Fwi);
*)
end;
Result := CallNextHookEx(gNextHook, nCode, AWParam, ALParam);
end;
I chose WM_INITDIALOG which is sent for native dialogs like the open or save dialogs, and CM_ACTIVATE which is sent when a VCL form is shown modally. There may be more such messages that need to be caught. Above code works for the MessageDlg() function, the Application.MessageBox() function and TOpenDialog at least.
Since these dialogs don't have their own taskbar button I added (commented out) code to flash the taskbar button of the main form as well. This isn't optimal, as they flash out of sync.
Tested with Delphi 2009 on Windows XP, all error handling omitted, use it as a starting point only.