@beppe thanks for your response, I want to use this when i receive an alert from AB on my email. I have configured my email on AB, so when there is an alert, AB sends an email to me. I need to attach a chart with this email i will have more clear idea.
@parthashah, in such a case you should trigger the OLE methods each time in your code there is a condition that at present will invoke the e-mail message sending (typically employing the AlertIf() or SendEMail() functions).
You should then replace the AmiBroker native method to send the mail with something similar to what I explained in this thread.
Probably, instead of using an AmiBroker batch job like I did in that example, you should handle the "send mail with attachment" action within your code via ShellExecute() to launch a Windows/Dos batch/script.
As an alternative, if you have Outlook on your PC (I mean the Windows executable Office application), you can use its Automation methods to create and send the email with an attachment, doing it entirely from AFL without the need to invoke external scripts.
Here is an example of Outlook automation calls from AFL:
// Some Outlook Automation predefined Constants
// See a full list, for example, here:
// https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2003/aa219371(v%3Doffice.11)
olMailItem = 0;
olTo = 1;
olCC = 2;
olFormatPlain = 1;
olFormatHTML = 2;
oOutlook = CreateObject( "Outlook.Application" );
if( typeof( oOutlook ) == "object" )
{
_TRACE( "Outlook Automation Object created." );
oMail = oOutlook.CreateItem( olMailItem );
oMail.Subject = "HTML e-mail from AmiBroker with chart attachment";
// Specify recipient address (optionally repeat to "add" further recipients)
oRecipients = oMail.Recipients;
oRecipient = oRecipients.Add( "your.email@outlook.com" );
oRecipient.Type = olTo;
oRecipient = oRecipients.Add( "youralternative.email@gmail.com" );
oRecipient.Type = olCC;
// Specify the file to attach (you can repeat the command to "add" multiple files)
oAttachments = oMail.Attachments;
oAttachments.Add( "C:\\Temp\\OLE_Exported_Chart.png" );
// Create the actual e-mail message
// To send as plain text uncomemnt this and comment out the HTML part
// oMail.BodyFormat = olFormatPlain;
// oMail.Body = Name() + - " + FullName() + " - " + Now( 0 );
// The body of this message need to be specified in valid HTML
oMail.BodyFormat = olFormatHTML;
oMail.HTMLBody = "<HTML>" +
"<BODY>" +
"<H2>" + Name() + "</H2>" +
"<H3>" + FullName() + "</H3>" +
"<H4>" + Now( 0 ) + "</H4>" +
"<p><small>" + "Chart/Signals commentary:" + "</small></p>" +
"<p><strong>" + "BUY NEXT BAR at OPEN." + "</strong></p>" +
"</BODY>" +
"</HTML>";
// Send the e-mail
oMail.Send;
_TRACE( "Outlook Automation Object - Message sent. OLE done." );
// See also Microsoft documentation about the eventual use of the Quit method (normally IS NOT required)
// The OLE object is released automatically at the end of the formula; no explicit freeing is necessary
}
Before using the above sample, change the recipients' addresses and the path/filename of the image to attach.
Search with Google "Send email with Outlook automation" to find further examples. You'll get results mainly in VBA, but looking at the above sample, you should be able to adapt them to AFL.
In any case, keep in mind that using OLE from AFL may impact on the performance of your tasks and generate the following message:
Warning 503. Using OLE / CreateObject / CreateStaticObject is not multi-threading friendly.
See the AmiBroker help to get the full explanation of it. Here below, for your convenience, I'm quoting the main points:
Your formula contains CreateObject and/or CreateStatic object calls. While they are still supported, the performance of OLE in multi-threaded applications is poor. ...
OLE was developed by Microsoft back in 1990's in the 16-bit days it is old technology, and it effectively prevents threads from running at full speed as all OLE calls must be served by one and only user-interface thread. ....
For this reason, if only possible you should strictly avoid using OLE / CreateObject in your formulas. ...
If you fail to do so, the performance will suffer. Any call to OLE from a worker thread causes posting a message to OLE hidden window and waiting for the main application UI thread to handle the request. If multiple threads do the same, the performance would easily degrade to single-thread level, because all OLE calls are handled by main UI thread anyway. ...
FWIW, compared to Outlook, OLE itself is fast.
@Tomasz, I think that to get rid of all these alternative methods, it would be better to have a native AFL function to export the images of charts (or ideally of single panes) and also a version of SendEmail () that can optionally include an attachment.
But considering the large number of pending features entries in the Feedback Center, I wonder if it's worth making a formal request.
While I can code in C++, I like to use C# for coding quick tasks and to automate some things. One large wish list item would be the ability to use C# (non-ole, non-late binding) with AB and not thru some third party solution either. For whatever reason, accessing the symbol data via a ole c# solution is so so slow.
C# is much faster than previously thought once the programmer takes manual control over GC.
-S
As I wrote, OLE is not "slow" in the sense of seconds. It is slow in a sense of single millisecond. Outlook is slow in a sense of (fraction of) a second. We are talking about different orders of magnitude. OLE mechanisms are 1000x faster than Outlook, so using OLE to control Outlook is not really something you should be worried about.
So as far as "automation" goes OLE is adequate.
Using it for something requiring millions of function calls is completely different story, though.
No I know, what I mean is that OLE thru C# seems very very slow, even as compared to Windows Script. Maybe it's my code?
Hi,
Hope all is well with you, many many months since I sent any mail to u.
Coding is not really my cup of tea, so sticking to using basic Amibroker facilities.
Came across this thread " Chart Export via AFL - OLE"
during routine week end - going through the forum !!
Have noticed that when the chart is exported THE GUI BUTTON does not print .
Just for curiosity sake -- is there any way this may be done - no specific reason !
well, bye for now, warm regards, take care.
@JEETU, you are right. Native Gui controls do not appear in exported images.
I suppose that this is because AB implements double buffering techniques and what is exported comes from some hidden canvas, but @Tomasz only can provide a qualified answer.
I'm sorry to hear that you gave up studying AFL programming, but maybe you started with a project that was a bit too ambitious (I refer to the Fibonacci retracements formula).
The only suggestion I can give you when you are ready to try again, is to start slowly, moving from the simplest examples that are available in the documentation to the more elaborate ones explained in the KB articles.
I am convinced that, given the necessary time, everyone can learn to program.
When learning something new seems too difficult to me, I remember a phrase I read many years ago in a French course: "Avec de la patience, on arrive à tout."
In Windows, all controls (buttons, edits, checkboxes, radio, tree, list views) are actually windows (hence the name of OS). So native GUI controls are separate windows that are not really part of chart - they live on "top" of chart.
The only way to "print" it with buttons and other GUIs is making a screenshot.
i want to export Image From ProfiT Chart But Its Show me error Msg ,
did anyone figure out a way to export charts from a floating window already? I know "you can't access floating charts via OLE because they are not MDI windows".
Would be nice though if one could export a chart just by pushing a button on a floating window. I can call the data I have in the floating window inside the docked window and then export the chart but it is a hassle because for 1 thing symbol lock should be turned off. So I support Beppe's suggestion for a native AFL function to export a chart unless there is some other way I don't know about yet. Or maybe the native function is already there and I did not notice
maybe a foolish question but I use this code @beppe posted with slight modifications for export path and export name. Works great but when I have multiple instances of Amibroker opened and drag this code for instance in a chart window of the second instance and press the "Export" button it will still export the chart in the first opened instance of Amibroker. Is there any way around this? Thanks
@empottasch, please see this previous answer of @Tomasz.
Re the "native" function, let's hope he will consider it.
Multiple users would like to export chart images. Personally, I would like to see an option to apply it complete charts or single panes.
OLE is a bit of pain for some users. I recall another post where @Tomasz wrote about the possibility of replacing it with some other way to control the application, but unfortunately, I suspect that it is not a priority.
As I wrote, floating windows are not MDI and they are not "active" in the MDI sense, therefore if you use AB.ActiveWindow you will get MDI active window, not floating window. The thing is that Microsoft never provided a system (OS) way to have MDI in multiple monitors therefore developers have to workaround MDI limitations creating those "floating" windows that live outside Microsoft MDI framework. What I can do is to give access to that floating window from OLE and then you will be able to call ExportImage on it.
thanks for the replies. That would be great that addition to OLE. My last question however was a bit different from the the one I asked in May 2020. Today my question is unrelated to floating windows.
Today my problem was that if I open 1 instance of Amibroker then Beppes code works good. If I then open another instance of Amibroker (so I have Amibroker running twice on a single computer) then Beppes code still works (inside the second instance) but it sees the window in the first instance as the active window. So if I press the export button inside the second instance of Amibroker it exports the chart from the first instance of Amibroker.
No big issue I just wondered if I can control this from the code.
That is how Windows OLE works, CreateObject() gives you OLE dispatch interface of first running instance within given Windows user account. The only way is to run two instances in two separate Windows user accounts.
ok thanks for your reply
That's great. I would be also interested in this feature. Currently I'm using an AFL code similar to this one:
if( some event )
{
AB = CreateObject( "Broker.Application" );
doc = AB.Documents( 3 );
doc.Name = NewStock; // for instance "CDR"
doc = AB.Documents( 6 );
doc.Name = NewStock;
doc = AB.Documents( 10 );
doc.Name = NewStock;
}
... to change tickers in selected chart windows (which are usually in floating mode) to automatically synchronize them with the news coming during the session from different sources. So when a news for a stock appears, upper code automatically and instantly changes tickers in selected charts to this stock. It's very useful for an active daytrader. One of such windows is my custom trading panel which lets me quickly place an order (currently manually but shortly also in automatic mode). It works well but it is not the most robust solution, because whether automatic change of the ticker is successful in some cases depends on factors like:
- what was the last active element - was it a chart, an analysis window or some other element
- are the selected charts and last active element in normal or floating mode.
- if I close or add a new chart window to my layout, upper code might synchronize wrong chart(s).
I have also tried synchronizing only one selected chart and let AmiBroker sync other selected charts using a Symbol link
feature, but it needs user interraction to work as expected.
I have also noticed that when I'm connected to Statica plugin (and using such code) - after successfully changing the ticker, in some cases the ticker returnes to the previous one. It happens for instance when the last active window was in a floating mode. This problem doesn't occur when I'm working on a local database.
So summing up - currently the upper code does the job and it is very useful for me, but there are many factors which I need to take into account for the changing symbol to work properly.
If there was a way to have a direct robust access to floating windows from OLE it would probably make my solution better.
Thank you.
FWIW: Analysis (and other non-chart windows like Account/ Web research) are NOT part of Documents collection so presence of Analysis windows does not impact Documents collection. Analysis windows are present in OTHER collection: AnalysisDocs.