Is there any onClose event in OpenFL? - haxe

I am using a library which needs to be cleaned up at exit time (i.e. closing a socket so that it does not hang until some timeout) in an OpenFL 2.2.1 application.
However, I could not find any event which is called when I close the window with Alt+F4 or the closing button of the window.
How can I detect that the application is terminating, in order to clean my resources?

To answer your question about openfl-next, there is the lime.app.Application.onExit event, which it inherits from lime.app.Module. An Application reference is stored in openfl.display.Stage.application instance field.
So, a multi-version variant of the function would be as following:
static function setExitHandler(func:Void->Void):Void {
#if openfl_legacy
openfl.Lib.current.stage.onQuit = function() {
func();
openfl.Lib.close();
};
#else
openfl.Lib.current.stage.application.onExit.add(function(code) {
func();
});
#end
}
And then you would just
setExitHandler(function() {
trace("Quit!");
});

Part of an answer would be to fallback to legacy OpenFL (with -Dopenfl-legacy or <haxedef name="openfl-legacy" />).
This trick forces the compiler into using the former API, which enables Lib.stage.onQuit.
public static function main():Void
{
// …
Lib.stage.onQuit = onClose;
// …
}
private static function onClose()
{
// …
// Cleanup of opened resources
// …
Lib.close();
}
Be sure to call Lib.close() or your window won't be closeable any more!
However, this does not answer my question for the newer openfl-next.

Related

Cocos2d-x Multithreading sceanrio crashes the game

my scenario is simple:i made a game using cocos2d-x and i want to download images (FB and Google play) for multi player users and show them once the download is done as texture for a button.
in ideal world, things work as expected.
things get tricky when those buttons got deleted before the download is done.
so the callback function is in weird state and then i get signal 11 (SIGSEGV), code 1 (SEGV_MAPERR)
and the app crashes
This is how i implmented it
I have a Layout class called PlayerIcon. the cpp looks like this
void PlayerIcon::setPlayer(string userName, string displayName, string avatarUrl){
try {
//some code here
downloadAvatar(_userName, _avatarUrl);
//some code here
}
catch(... ){
}
}
void PlayerIcon::downloadAvatar(std::string _avatarFilePath,std::string url) {
if(!isFileExist(_avatarFilePath)) {
try {
auto downloader = new Downloader();
downloader->onFileTaskSuccess=CC_CALLBACK_1(PlayerIcon::on_download_success,this);
downloader->onTaskError=[&](const network::DownloadTask& task,int errorCode,
int errorCodeInternal,
const std::string& errorStr){
log("error while saving image");
};
downloader->createDownloadFileTask(url,_avatarFilePath,_avatarFilePath);
}
catch (exception e)
{
log("error while saving image: test");
}
} else {
//set texture for button
}
}
void PlayerIcon::on_download_success(const network::DownloadTask& task){
_isDownloading = false;
Director::getInstance()->getScheduler()-> performFunctionInCocosThread(CC_CALLBACK_0(PlayerIcon::reload_avatar,this));
}
void PlayerIcon::reload_avatar(){
try {
// setting texture in UI thread
}
catch (...) {
log("error updating avatar");
}
}
As i said, things works fine until PlayerIcon is deleted before the download is done.
i dont know what happens when the call back of the download task point to a method of un object that s deleted (or flagged for deletion).
i looked in the downloader implementation and it doesn't provide any cancellation mechanism
and i'm not sure how to handle this
Also, is it normal to have 10% crash rate on google console for a cocos2dx game
any help is really appreciated
Do you delete de Downloader in de destructor of the PlayerIcon?
there is a destroy in the apple implementation witch is trigered by the destructor.
-(void)doDestroy
{
// cancel all download task
NSEnumerator * enumeratorKey = [self.taskDict keyEnumerator];
for (NSURLSessionDownloadTask *task in enumeratorKey)
{
....
DownloaderApple::~DownloaderApple()
{
DeclareDownloaderImplVar;
[impl doDestroy];
DLLOG("Destruct DownloaderApple %p", this);
}
In the demo code of cocos2d-x: DownloaderTest.cpp they use:
std::unique_ptr<network::Downloader> downloader;
downloader.reset(new cocos2d::network::Downloader());
instead of:
auto downloader = new Downloader();
It looks like you are building this network code as part of your scene tree. If you do a replaceScene/popScene...() call, while the async network software is running in the background, this will cause the callback to disappear (the scene will be deleted from the scene-stack) and you will get a SEGFAULT from this.
If this is the way you've coded it, then you might want to extract the network code to a global object (singleton) where you queue the requests and then grab them off the internet saving the results in the global-object's output queue (or their name and location) and then let the scene code check to see if the avatar has been received yet by inquiring on the global-object and loading the avatar sprite at this point.
Note, this may be an intermittent problem which depends on the speed of your machine and the network so it may not be triggered consistently.
Another solution ...
Or you could just set your function pointers to nullptr in your PlayerIcon::~PlayerIcon() (destructor):
downloader->setOnFileTaskSuccess(nullptr);
downloader->setOnTaskProgress(nullptr);
Then there will be no attempt to call your callback functions and the SEGFAULT will be avoided (Hopefully).

SHGetFileInfo doesn't return correct handle to icon

I'm using SHGetFileInfo function for getting icons for folders and different file types. According to MSDN call of this function should be done from background thread and before call Component Object Model (COM) must be initialized with CoInitialize or OleInitialize.
My code looks like this:
public void SetHlinkImage(string path)
{
Shell32.OleInitialize(IntPtr.Zero);
Task task = Task.Factory.StartNew(() => { LoadIcons(path); });
}
private void LoadIcons(string path)
{
image = GetHlinkImage(path);
if (OwnerControl.InvokeRequired)
layout.ModuleControl.BeginInvoke((MethodInvoker)delegate ()
{
Shell32.OleUninitialize();
});
}
public Icon GetHlinkImage(string path)
{
uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_ATTRIBUTES | Shell32.SHGFI_SMALLICON;
Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
IntPtr result = Shell32.SHGetFileInfo(path,
Shell32.FILE_ATTRIBUTE_DIRECTORY,
ref shfi,
(uint)Marshal.SizeOf(shfi),
flags);
Icon icon = (Icon)Icon.FromHandle(shfi.hIcon).Clone();
WinApi.DestroyIcon(shfi.hIcon); // cleanup
return icon;
}
Mostly the problem appears after first call of the code and as result I get an exception when I tried to create Icon from icon handle:
System.ArgumentException: Win32 handle that was passed to Icon is not
valid or is the wrong type
And further calls of the code work without problems.
Actually behaviour also somehow depends on the test system. It is hardly possible to reproduce this issue on Windows10 systems but on Windows 7 it happens quite often.
Has anyone experienced this problem?
From comment of Hans Passant:
Calling OleInitialize() is pointless, the CLR already initializes COM before it starts a thread. And it failed, something you cannot see because you are not checking its return value. Not knowing that, it just spirals into undiagnosable misery from there. Yes, more of it on Win7. You must provide an STA thread, if this needs to run in the background then consider this solution.

How to enable user to re-entry(or) reuse the same dialog in MFC

I'm currently doing a FTP download using MFC. Is a very simple program which takes 2 inputs from user and click a download button in order to download from server. Everything is fine and im able to download it from. But i realized this program can only be executed once. Either successful or fail user has to open the .exe again to download another file. I'm a beginner in C&C++ with a simple knowledge i put OnInitDialog() at the last line of the download function hopping it will loop back and initialize again. Of course it doesn't work. Below are my current codes for the download button
BOOL CFTPDOWNLOADDlg::Log_In(char* path, char* ID, char* password {
m_pFtpConnection = NULL;
try{
// path
// ID
// password
m_pFtpConnection = m_Session.GetFtpConnection(path,
ID,password,INTERNET_INVALID_PORT_NUMBER);
}
catch(CInternetException *pEx){
pEx->ReportError(MB_ICONEXCLAMATION);
m_pFtpConnection = NULL;
pEx->Delete();
return FALSE;
}
return TRUE;
}
BOOL CFTPDOWNLOADDlg::Download(){
m_Edit3.SetWindowText("Downloading..");
m_Session.EnableStatusCallback(TRUE);
if(m_pFtpConnection->GetFile(serv_Loc,host_Loc,
FALSE,FILE_ATTRIBUTE_NORMAL,FTP_TRANSFER_TYPE_BINARY,1) != 0){
MessageBox("Download Complete");
m_Edit3.SetWindowText("");}
else{
MessageBox("Download Fail");
return FALSE;
}
// Log_out Session
m_Session.Close();
m_pFtpConnection->Close();
if(m_pFtpConnection!=NULL) delete m_pFtpConnection;
else MessageBox("Download Complete");
return TRUE;
}
BOOL CFTPDOWNLOADDlg::get_Path(){
...
...
...
sprintf(serv_Loc,"soft\\%s\\%d\\%s.zip",s_No,r_Number,r_No);
sprintf(host_Loc,"%s\\%s.zip",buff2,r_No);
return TRUE;
}
void CFTPDOWNLOADDlg::OnCancel() {
// Log_out Session
m_Session.Close();
m_pFtpConnection->Close();
if(m_pFtpConnection!=NULL)
delete m_pFtpConnection;
CDialog::OnCancel();
}
void CFTPDOWNLOADDlg::OnDLButton() {
//get path from user input
get_Path();
// start download
Download();
}
I've tried to search online, i couldn't find anything which is close. Sorry for my poor explanation.
Thank you in advance for your kindness in replying
Here is what you need to do:
You should make CInternetSession m_Session; a member of your CWinApp-derived class.
You should call m_Session.Close() in ExitInstance() method of your CWinApp-derived class.
In your CDialog-derived class you should only deal with CFtpConnection related stuff. So when user clicks on Download button you should call GetFtpConnection() and initialize your m_pFtpConnection and do the rest. When download/upload is done call m_pFtpConnection->Close(); and delete m_pFtpConnection;
Please also use CString instead of char*. There are lots of benefits like automatic UNICODE support, etc.
Please also consider using CString::Format() method instead of sprintf().
You should also consider using threads to perform upload/download tasks in a separate worker thread. Use AfxBeginThread() to start the thread. This way you'll not affect Windows message pump that is a part of main application (GUI) thread. So your GUI wont lock up while you uploading/downloading files.

Cross Thread UI

I have an issue with cross threading on a UI. I have read all the ways to do it and have implemented them as seen below.
public void UpdateList(object obj)
{
// do we need to switch threads?
if (listBox1.InvokeRequired)
{
MethodInvoker del = () => UpdateList(obj);
this.Invoke(del);
return;
}
// ok so now we're here, this means we're able to update the control
// so we unbox the object into a string
string text = (string)obj;
// and update
listBox1.Items.Add(text);
}
The issue comes when I try to do a
hubConnection.Start().Wait();
After that call I am trying to update my list.
Without the wait is fine. When I add the Wait it hangs on the UpdateList Invoke. There is no error...it just hangs.
I am handling this call in a button event.
Wait() is creating a deadlock on the mainthread.
Replace the hubconnection.Start.Wait() with:
await hubconnection.Start() in an async method:
public void async StartHubClickedEvent(...){
await hubconnection.Start()
}
The Microsoft Async library enables use of async/awaut on .net 4.0 and VS12.
Install-Package Microsoft.Bcl.Async
See Deadlock when thread uses dispatcher and the main thread is waiting for thread to finish
You've generated a recursive loop. Assuming an Invoke is Required, you'll call up the same method, hit if (listBox1.InvokeRequired) again (which will still pass true) and start looping as you keep calling up the same method again and again. It's better to do an If..Else pattern here where you directly invoke the change on the ListBox or simply perform the change without the invoke
An Example
if (listBox1.InvokeRequired)
{
listBox1.Invoke(()=> { listBox1.Items.Add((string)text) };
}
else
{
string text = (string)obj;
// and update
listBox1.Items.Add(text);
}

setTimer() to generate WM_TIMER message only once

I am using timers with resource ID added and based on WM_TIMER message.
I would like to call a routine like DrunkenDragon() on OnTimer() but for only once after SetTimer(id,10sec,NULL) was called. We know that Call to KillTimer() inside DrunkenDragon() routine would fix the solution. Is it okay to go with this, or am I missing out something great with timers.
(Only answering this in case someone else comes across it like I did and was unsatisfied with the answers available)
So, in WindowClass.h, what you can do is an enumeration of the timer identifiers you want to use. While you certainly can use raw numeric values, using symbols is probably easier to work with in the long run.
class WindowClass : CWnd
{
// other parts of the interface...
protected:
enum
{
TIMER_MAIN_UPDATE = 1,
TIMER_PLASTERED_DRAGON
};
};
Meanwhile, back at in WindowClass.cpp,
int WindowClass::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
// { ... other initialization code }
// In case you want to do other types of updates at regular intervals.
SetTimer(TIMER_MAIN_UPDATE, 1000, NULL);
// Note the symbolic identifiers.
SetTimer(TIMER_PLASTERED_DRAGON, 10000, NULL);
return 0;
}
That's only any good if you want to do it 10 seconds after the window's been created, though. You can also just call SetTimer() in some other event handler whenever you'd like:
void WindowClass::OnJustGotPaid()
{
// { ... other handling }
// Since our dragon is a real lightweight, it apparently only takes
// 10 seconds to get him puking up flaming vomit.
SetTimer(TIMER_PLASTERED_DRAGON, 10000, NULL);
}
When it comes time for the actual event to be handled, it is typically handled in the Windows OnTimer() callback. A timer event can be directed to a different (custom) callback, if desired, by specifying a valid function pointer in SetTimer()'s third parameter instead of NULL.
void WindowClass::OnTimer(UINT_PTR p_timer_id)
{
switch(p_timer_id)
{
default:
break;
case TIMER_MAIN_UPDATE:
// { ... main update code }
break;
case TIMER_PLASTERED_DRAGON:
// Killing the timer first in case DrunkenDragon() takes a good
// long while for whatever reason.
KillTimer(TIMER_PLASTERED_DRAGON);
DrunkenDragon();
break;
}
}
int CYourDialog::OnInitDialog()
{
__super::OnInitDialog();
SetTimer(0x10, 10000, NULL);
return true;
}
void CYourDialog::OnTimer(UINT_PTR ignore)
{
DrunkenDragon();
}
And ensure you have ON_WM_TIMER in message-map.
You are not missing anything and you would have to use KillTimer for system to stop generating WM_TIMER messages.
You can also use CreateTimerQueueTimer and set parameters the way a callback is called only once.
See this for more details.

Resources