I am desparately in search of MFC dll sample that updates an edit field on a dialog derived from CDialog with a string using only the callback function. I realize static functions are the ones that are used in the calback.
I guess you realise that the dll should have something (like a separate thread, or a certain third-party message handler for this model. So, this is out of scope for this question.
Back to the question:
In order to create a callback function you need to "typedef" its prototype and pass the address of this function from your calling app to the dll. In your dll "h" file specify this:
// This is the Mfcdll1.h header file
// You should have some code created by the wizard, similar to the following:
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
// This is the typedef for your callback function type:
typedef void (CALLBACK* MY_DLL_CALLBACK)
(
LPVOID lpParam,
LPCTSTR lpszMessage
);
class CMyDllApp : public CWinApp
{
public:
CMyDllApp();
// etc.
void SetCallBack(MY_DLL_CALLBACK pCallback, LPVOID pCallbackParm){m_pCallback = pCallback; m_pCallbackParm = pCallbackParm;}
private:
MY_DLL_CALLBACK m_pCallback;
LPVOID m_pCallbackParm;
};
MYDLL_API void SetCallBack(MY_DLL_CALLBACK pCallback, LPVOID pCallbackParm);
In your dll's cpp file specify this:
CMyDllApp::CMyDllApp()
{
m_pCallback = NULL;
m_pCallbackParm = NULL;
}
MYDLL_API void SetCallBack(MY_DLL_CALLBACK pCallback, LPVOID pCallbackParm)
{
theApp.SetCallBack(pCallback, pCallbackParm);
}
Now, if your dll wants to call the callback function, all you need to do is:
CMyDllApp::SendCallbackToTheCaller(LPCTSTR lpszMessage)
{
if(m_pCallback) (*(m_pCallback))(m_pCallbackParm, lpszMessage);
}
At this stage you have done coding in the dll. Now, all you need to do is specify the static callback function in your dialog that will update you Edit control, similar to this:
In the h file:
// TestDlg.h - My Test dialog with the Edit Control
CTestDlg: public CDialog
{
public:
static void CALLBACK StatusCallback(LPVOID lpParam, LPCTSTR lpszMessage);
};
In the cpp file:
BOOL CTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Insert this line along your stuff
::SetCallback((MY_DLL_CALLBACK)StatusCallback, (LPVOID)this);
}
void CALLBACK CTestDlg::StatusCallback(LPVOID lpParam, LPCTSTR lpszMessage)
{
CTestDlg* pTestDlg = reinterpret_cast<CTestDlg*>(lpParam);
ASSERT(pTestDlg!=NULL);
pTestDlg->m_edStatus.SetWindowText(lpszMessage);
}
Related
OK, I am trying to use CFileDialog::AddCheckButton. The function call succeeds and I'm able to see the new check box. I'm unable to see any events and while I can override OnInitDialog, overriding OnOK is ignored. I'm not sure what I'm doing wrong:
//header
class CTPSaveDialog : public CFileDialog
{
DECLARE_DYNAMIC(CTPSaveDialog)
static const CString CTPSaveDialog::m_cstrFilter;
public:
BOOL m_bForce;
CTPSaveDialog(
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
CWnd* pParentWnd = NULL,
DWORD dwSize = 0);
~CTPSaveDialog();
virtual BOOL OnInitDialog();
DECLARE_MESSAGE_MAP()
afx_msg void OnBnClickedCheckForce();
virtual void OnOK();
};
// implementation
const CString CTPSaveDialog::m_cstrFilter = "JPEG images (*.jpg)|*.jpg|TIFF Format (*.tif)|*.tif|Windows Bitmap (*.bmp)|*.bmp|Portable Network Graphics (*.png)|*.png|GIF (*.gif)|*.gif||";
IMPLEMENT_DYNAMIC(CTPSaveDialog, CFileDialog)
CTPSaveDialog::CTPSaveDialog(LPCTSTR lpszDefExt, LPCTSTR lpszFileName, DWORD dwFlags, CWnd * pParentWnd, DWORD dwSize) :
CFileDialog(FALSE, lpszDefExt, lpszFileName, dwFlags, m_cstrFilter, pParentWnd, dwSize, TRUE)
{
AddCheckButton(IDC_CHK_FORCE, "Force", FALSE);
m_bForce = FALSE;
m_ofn.lpstrTitle = "Write Simulation To File";
}
CTPSaveDialog::~CTPSaveDialog()
{
}
BOOL CTPSaveDialog::OnInitDialog()
{
CFileDialog::OnInitDialog();
if (GetDlgItem(IDC_CHK_FORCE))
SendDlgItemMessage(IDC_CHK_FORCE, BM_SETCHECK, m_bForce ? BST_CHECKED : BST_UNCHECKED);
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
BEGIN_MESSAGE_MAP(CTPSaveDialog, CFileDialog)
ON_BN_CLICKED(IDC_CHK_FORCE, &CTPSaveDialog::OnBnClickedCheckForce)
END_MESSAGE_MAP()
void CTPSaveDialog::CTPSaveDialog()
{
m_bForce = !m_bForce;
}
void CTPSaveDialog::OnOK()
{
// TODO: Add your specialized code here and/or call the base class
CFileDialog::OnOK();
}
In CFileDialog with Vista style, windows messages are not handled in message map. Instead CFileDialog uses specific virtual functions. You only need to declare and define these functions.
Use OnCheckButtonToggled to detect if check box is clicked.
Use OnFileNameOK to detect when file is selected and Open/Save button is clicked.
Use SetCheckButtonState to set/unset the check button (not SendDlgItemMessage)
See CFileDialog for all available methods.
As stated in documentation, OnInitDialog is not supported either:
Some CFileDialog methods are not supported under Windows Vista or
later. Check the individual method topic for information about whether
the method is supported. In addition, the following inherited
functions are not supported under Windows Vista or later:
CDialog::OnInitDialog
...
Just do the initialization in the constructor or before calling DoModal(), and override these functions:
class CTPSaveDialog : public CFileDialog
{
...
virtual void OnCheckButtonToggled(DWORD dwIDCtl, BOOL bChecked);
virtual BOOL OnFileNameOK();
};
void CTPSaveDialog::OnCheckButtonToggled(DWORD dwIDCtl, BOOL bChecked)
{
if (dwIDCtl == IDC_CHK_FORCE)
TRACE("Is checked? %d\n", bChecked);
}
BOOL CTPSaveDialog::OnFileNameOK()
{
TRACE("Clicked Open/Save button\n");
//return FALSE to close the dialog
return FALSE;
}
I know I can use Win32 APIs for accessing files within my own local data folder (eg, see this answered question) but I need to access files outside of my app (eg, from the Pictures Library) and the libraries I'm trying to use are all based on Win32 file HANDLEs and / or they rely on using relative filenames.
Since the only way to get at files in the Pictures Library (or to get files / folders returned from a picker) is via StorageFile objects, how can I re-use my existing code? Do I have to re-write it all to be asynchronous and rely on the WinRT storage APIs?
Starting in the "Anniversary Update" (aka "RS1" or build 10.0.14393) you are able to get a Win32 HANDLE from a StorageItem (file or folder) and to create new named files (returning a HANDLE) from within a StorageFolder. You do this using the new IStorageFolderHandleAccess and IStorageItemHandleAccess APIs.
Note: These APIs have been accidentally placed inside the WINAPI_PARTITION_DESKTOP partition (they're not desktop-specific; they're available to UWPs). This will be addressed in future SDK updates.
To use one of these new COM interfaces, you simply QI the StorageFile or StorageFolder for the interface. If the interface isn't supported, it means your app is running on a down-level OS (or perhaps the Storage Item isn't actually backed by a real file, but is rather a pseudo-file). You can use these interfaces from C++ (C++/CX or WRL) or from C#.
Here's a simple example of using a FolderPicker to have the user pick a location on their disk (which returns a brokered StorageFolder object) and then using Win32 APIs ReadFile and WriteFile to read and write a file from that location.
As noted above, we have to copy the declarations for the interface into our own code because the real SDK versions are in the wrong API partition. (I would advise against modifying the SDK files to solve the problem). So first up is our own header file, eg StorageHandleAccess.h, that copies the declarations from the SDK file WindowsStorageCOM.h:
#pragma once
// These are copied from WindowsStorageCOM.h
// You can remove this header file once the real file has been updated
// to fix the WINAPI_PARTITION_DESKTOP block
typedef interface IOplockBreakingHandler IOplockBreakingHandler;
typedef interface IStorageItemHandleAccess IStorageItemHandleAccess;
typedef interface IStorageFolderHandleAccess IStorageFolderHandleAccess;
#ifdef __cplusplus
extern "C" {
#endif
typedef /* [v1_enum] */
enum HANDLE_OPTIONS
{
HO_NONE = 0,
HO_OPEN_REQUIRING_OPLOCK = 0x40000,
HO_DELETE_ON_CLOSE = 0x4000000,
HO_SEQUENTIAL_SCAN = 0x8000000,
HO_RANDOM_ACCESS = 0x10000000,
HO_NO_BUFFERING = 0x20000000,
HO_OVERLAPPED = 0x40000000,
HO_WRITE_THROUGH = 0x80000000
} HANDLE_OPTIONS;
DEFINE_ENUM_FLAG_OPERATORS(HANDLE_OPTIONS);
typedef /* [v1_enum] */
enum HANDLE_ACCESS_OPTIONS
{
HAO_NONE = 0,
HAO_READ_ATTRIBUTES = 0x80,
HAO_READ = 0x120089,
HAO_WRITE = 0x120116,
HAO_DELETE = 0x10000
} HANDLE_ACCESS_OPTIONS;
DEFINE_ENUM_FLAG_OPERATORS(HANDLE_ACCESS_OPTIONS);
typedef /* [v1_enum] */
enum HANDLE_SHARING_OPTIONS
{
HSO_SHARE_NONE = 0,
HSO_SHARE_READ = 0x1,
HSO_SHARE_WRITE = 0x2,
HSO_SHARE_DELETE = 0x4
} HANDLE_SHARING_OPTIONS;
DEFINE_ENUM_FLAG_OPERATORS(HANDLE_SHARING_OPTIONS);
typedef /* [v1_enum] */
enum HANDLE_CREATION_OPTIONS
{
HCO_CREATE_NEW = 0x1,
HCO_CREATE_ALWAYS = 0x2,
HCO_OPEN_EXISTING = 0x3,
HCO_OPEN_ALWAYS = 0x4,
HCO_TRUNCATE_EXISTING = 0x5
} HANDLE_CREATION_OPTIONS;
EXTERN_C const IID IID_IOplockBreakingHandler;
MIDL_INTERFACE("826ABE3D-3ACD-47D3-84F2-88AAEDCF6304")
IOplockBreakingHandler : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE OplockBreaking(void) = 0;
};
EXTERN_C const IID IID_IStorageItemHandleAccess;
MIDL_INTERFACE("5CA296B2-2C25-4D22-B785-B885C8201E6A")
IStorageItemHandleAccess : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE Create(
/* [in] */ HANDLE_ACCESS_OPTIONS accessOptions,
/* [in] */ HANDLE_SHARING_OPTIONS sharingOptions,
/* [in] */ HANDLE_OPTIONS options,
/* [optional][in] */ __RPC__in_opt IOplockBreakingHandler *oplockBreakingHandler,
/* [system_handle][retval][out] */ __RPC__deref_out_opt HANDLE *interopHandle) = 0;
};
EXTERN_C const IID IID_IStorageFolderHandleAccess;
MIDL_INTERFACE("DF19938F-5462-48A0-BE65-D2A3271A08D6")
IStorageFolderHandleAccess : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE Create(
/* [string][in] */ __RPC__in_string LPCWSTR fileName,
/* [in] */ HANDLE_CREATION_OPTIONS creationOptions,
/* [in] */ HANDLE_ACCESS_OPTIONS accessOptions,
/* [in] */ HANDLE_SHARING_OPTIONS sharingOptions,
/* [in] */ HANDLE_OPTIONS options,
/* [optional][in] */ __RPC__in_opt IOplockBreakingHandler *oplockBreakingHandler,
/* [system_handle][retval][out] */ __RPC__deref_out_opt HANDLE *interopHandle) = 0;
};
#ifdef __cplusplus
}
#endif
Next up is a simple usage of the API. This example takes a StorageFolder, a filename, and a creation flag (open or create) and tries to open (or create) the named file, reads (or writes) some text from (to) the file, and writes some output to the Debug console.
The code isn't particularly useful in a real-world setting, but illustrates how to use the API. This can be used in a blank C++ XAML project to replace the MainPage.xaml.cpp file (you should only need to update the namespace):
#include "pch.h"
#include "MainPage.xaml.h"
#include <ppltasks.h>
// TODO: Replace with your namespace
#error Replace this with your real namespace
using namespace FileHandleFromStorageFolder;
// Uncomment out this line and delete the next line once the SDK is fixed
//#include <WindowsStorageCOM.h>
#include "StorageHandleAccess.h"
// For ComPtr<>
#include <wrl\client.h>
// For HandleT<>
#include <wrl\wrappers\corewrappers.h>
__declspec(noreturn) inline void ThrowWithHRESULT(HRESULT hr, const wchar_t* message)
{
using namespace Platform;
throw ref new Exception(hr, ref new String(message));
}
__declspec(noreturn) inline void ThrowWithGetLastError(const wchar_t* message)
{
using namespace Platform;
throw ref new Exception(HRESULT_FROM_WIN32(GetLastError()), ref new String(message));
}
// Test is a simple test function. Pass in one of the HANDLE_CREATION_OPTIONS values
// (eg, HCO_CREATE_ALWAYS or HCO_OPEN_ALWAYS) and the function will try and either
// write to the file (if it's empty) or read from it (if it's not).
void Test(Windows::Storage::StorageFolder^ folder, const wchar_t* filename, HANDLE_CREATION_OPTIONS options)
{
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
// Get an IUnknown from the ref class, and then QI for IStorageFolderHandleAccess
ComPtr<IUnknown> abiPointer(reinterpret_cast<IUnknown*>(folder));
ComPtr<IStorageFolderHandleAccess> handleAccess;
HRESULT hr = abiPointer.As(&handleAccess);
if (FAILED(hr))
ThrowWithHRESULT(hr, L"Can't QI");
// Standard RAII wrapper for HANDLEs that represent files
HandleT<HandleTraits::FileHandleTraits>win32fileHandle;
// This is roughly equivalent of calling CreateFile2
hr = handleAccess->Create(filename, options,
HANDLE_ACCESS_OPTIONS::HAO_WRITE | HANDLE_ACCESS_OPTIONS::HAO_READ,
HANDLE_SHARING_OPTIONS::HSO_SHARE_NONE, HANDLE_OPTIONS::HO_NONE, nullptr,
win32fileHandle.GetAddressOf());
if (FAILED(hr))
ThrowWithHRESULT(hr, L"Can't access file");
// From here, it's standard Win32 code - nothing WinRT specific at all
LARGE_INTEGER size{ 0 };
if (FALSE == GetFileSizeEx(win32fileHandle.Get(), &size))
ThrowWithGetLastError(L"Can't get file size");
static const DWORD BUFFER_SIZE = 500;
char buffer[BUFFER_SIZE];
DWORD bytesUsed{ 0 };
if (size.QuadPart == 0)
{
const static auto str = "Hello, world\r\n";
if (FALSE == WriteFile(win32fileHandle.Get(), str, strlen(str), &bytesUsed, nullptr))
ThrowWithGetLastError(L"Can't write to file");
sprintf_s(buffer, ARRAYSIZE(buffer), "Wrote %d bytes to file\r\n", bytesUsed);
OutputDebugStringA(buffer);
}
else
{
if (FALSE == ReadFile(win32fileHandle.Get(), buffer, ARRAYSIZE(buffer) - 1, &bytesUsed, nullptr))
ThrowWithGetLastError(L"Can't read from file");
buffer[bytesUsed] = 0;
OutputDebugStringA(buffer);
}
}
// Trivial driver that gets a StorageFolder and then creates a file
// inside it, writes some text, then re-opens it to read text.
void TestWrapper()
{
using namespace Windows::Storage;
using namespace Windows::Storage::Pickers;
auto picker = ref new FolderPicker();
picker->FileTypeFilter->Append(L".txt");
picker->SuggestedStartLocation = PickerLocationId::Desktop;
concurrency::create_task(picker->PickSingleFolderAsync()).then([]
(StorageFolder^ folder)
{
if (folder != nullptr)
{
// Create and then read back a simple file
Test(folder, L"win32handletest.txt", HANDLE_CREATION_OPTIONS::HCO_CREATE_ALWAYS);
Test(folder, L"win32handletest.txt", HANDLE_CREATION_OPTIONS::HCO_OPEN_ALWAYS);
}
}
);
}
MainPage::MainPage()
{
InitializeComponent();
TestWrapper();
}
//In A.h I have the following code
include "afxwin.h"
include "msclr\auto_gcroot.h"
using namespace System;
using msclr::auto_gcroot;
namespace A
{
public ref class A
{
public:
virtual bool Func();
A();
~A();
virtual bool Connect();
protected:
DWORD WINAPI threadConnect(void* pParam);
};
public class AHelper
{
public:
auto_gcroot A;
};
}
//In A.cpp I have below code
// This is the main DLL file.
include "stdafx.h"
include "A.h"
include "string"
include "sstream"
include "stdlib.h"
include "strsafe.h"
include "windows.h"
include "tchar.h"
namespace A
{
A::A()
{
m_FuncHandle = mpsNil;
}
A::~A()
{
}
bool A::Func()
{
return true;
}
bool A::Connect()
{
AHelper* AHelper;
m_retVal = false;
AHelper = new AHelper();
AHelper->A = this;
HANDLE Handle_Of_Thread = 0;
DWORD dwThreadId;
//DWORD WINAPI threadConnect(void* pParam);
//If I declare the function declaration here I am getting
//error LNK2001: unresolved external symbol "unsigned long __stdcall threadConnect(void *)"
(?threadConnect##YGKPAX#Z)
Handle_Of_Thread = CreateThread
(NULL, 0, threadConnect, AHelper, 0, &dwThreadId); // with this code I am getting
//error C3867: 'A::A::threadConnect': function call missing argument list; use '&A::A::threadConnect' to create a pointer to member
return m_retVal;
}
DWORD WINAPI A::threadConnect(void* pParam)
{
AHelper* AHelper = reinterpret_cast(pParam);
//Here I need to call Func
return 0;
}
}
the usual trick is to have a static function that takes an id of some sort, this function determines which someFunc() to call (as each object will have its own someFunc, you obviously need to know which one) either using a lookup, or as is common in C/C++ apps, by passing the address of the object directly.
so you have something like:
static bool thread_func(object o)
{
return o->someFunc();
}
The trick is that the static function must be reentrant, so holds no state itself for the threads to interfere with (or if it does, make sure its thread safe)
That all supposes you're calling a method on an object that was not created within the thread. If you're just calling a function and you already have the object created within your thread, just call the function!
I have some functions in c and I would use this in a .net application.
For this I wrote an Wrapper class with C++/cli.
In the c interface is a callback function and wrapped this in a .net delegate.
But how should I release the unmanaged ressources for the callback gcHandle?
Is it allowd to call IsAllocated and Free from a GCHandle in the finalizer?
Because it is an managed ressource and is it possibile that the gc already release it?
Here is the code for the c interface:
// C functions
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*my_native_callback)(const uint8_t buffer[], uint32_t buffer_len);
void register_callback(my_native_callback c, uint32_t* id);
void remove_callback(uint32_t id);
#ifdef __cplusplus
}
#endif
And here the .net wrapper:
// .net wrapper (c++/cli)
public ref class MyWrapper
{
public:
MyWrapper()
{
RegisterCallback();
}
// Destructor.
~MyWrapper()
{
this->!MyWrapper();
}
protected:
// Finalizer.
!MyWrapper()
{
RemoveCallback(); // <- Is this safe?
// ... release other unmanaged ressorces
}
private:
void RegisterCallback()
{
uint32_t id = 0;
callbackDelegate_ = gcnew MyCallbackDelegate(this, &MyWrapper::OnCallback);
callbackHandle_ = System::Runtime::InteropServices::GCHandle::Alloc(callbackDelegate_);
System::IntPtr delegatePointer = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(callbackDelegate_);
register_callback(static_cast<my_native_callback>(delegatePointer.ToPointer()), &id);
callbackId_ = id;
}
void RemoveCallback()
{
if (callbackId_)
{
remove_callback(callbackId_);
callbackId_ = 0;
}
if (callbackHandle_.IsAllocated) // It this safe in the finalizer?
{
callbackHandle_.Free(); // It this safe in the finalizer?
}
callbackDelegate_ = nullptr; // It this safe in the finalizer?
}
void OnCallback(const uint8_t* buffer, uint32_t buffer_len)
{
// ...
}
private:
[System::Runtime::InteropServices::UnmanagedFunctionPointer(System::Runtime::InteropServices::CallingConvention::Cdecl)]
delegate void MyCallbackDelegate(const uint8_t* buffer, uint32_t buffer_len);
MyCallbackDelegate^ callbackDelegate_;
System::Runtime::InteropServices::GCHandle callbackHandle_;
int callbackId_;
// ...
};
Is the code snippet safe and what is best practice for this?
Thank you in advance.
There is no need to add an extra GCHandle reference to the delegate object. You are already correctly store a reference in the callbackDelegate_ field, enough to convince the garbage collector that the delegate is in use and should not be collected. No additional reference is required.
Just remove callbackHandle_ from your code.
I make a interface class in C++ for voice recognition, i´m using the Julius API. http://julius.sourceforge.jp/en_index.php?q=index-en.html.
Well, my class has some events, these events will be triggered by the Julius API.
The Julius API has the function call callback_add with this signature:
int callback_add (Recog *recog, int code, void(*func)(Recog *recog, void *data), void data)
I using some 'proxy' functions to Invoke the events and passing this functions to callback_add.
If the property event is static, it works fine, but if is a non static, inside the proxy function the property not be recognized.
The difficult is because I have to use the callback_add function and can't modify this.
Here is a summary of the class with 2 events (static and non-static)
Header
#ifndef FALAENGINE_H_
#define FALAENGINE_H_
#pragma once
extern "C"{
#include <julius/julius.h>
}
namespace FalaAPI {
public ref class FalaEngine
{
public:
FalaEngine();
~FalaEngine();
// Events
delegate void OnRecognizedDele(FalaAPI::RecoResult^ result);
static property OnRecognizedDele^ OnRecognized;
delegate void OnEngineStartDele();
property OnEngineStartDele^ OnEngineStart;
private:
Recog *recog;
Jconf *jconf;
};
}
#endif /* FALAENGINE_H_*/
Source
#include "stdafx.h"
using System::String;
using System::Console;
#include "FalaEngine.h"
#include <windows.h>
namespace FalaAPI{
void StartOnEngineStart()(Recog *recog, void * dummy){
if(FalaEngine::OnEngineStart->GetInvocationList()->Length > 0)
FalaEngine::OnEngineStart->Invoke();
}
void StartOnRecognized()(Recog *recog, void * dummy){
if(FalaEngine::OnRecognized->GetInvocationList()->Length > 0)
FalaEngine::OnRecognized->Invoke();
}
FalaEngine::FalaEngine(){
recog = j_recog_new();
jconf = j_jconf_new();
//Julius callback Functions
callback_add(recog, CALLBACK_EVENT_PROCESS_ONLINE, StartOnEngineStart, NULL);
callback_add(recog, CALLBACK_RESULT, StartOnRecognized, NULL);
}
}
The problem occurs inside StartOnEngineStart function:
error C2227: left of '->GetInvocationList' must point to class/struct/union/generic type
A non-static member exists separately in each instance. You haven't specified which instance contains the delegate you want to inspect, you've only specified a class (and there may be many instances).
Try using the dummy parameter to pass your instance. But be careful, because the garbage collector will move objects around unless you have pinned them, so simply passing the address will not work. You need to create and pass a GCHandle instead. (Be careful not to leak the GCHandle, or your object will never be released)
Something like this should be effective:
ref class FalaEngine;
struct EngineHandle
{
gcroot<FalaEngine^> handle;
EngineHandle(FalaEngine^ engine) : handle(engine) {}
};
public ref class FalaEngine
{
clr_scoped_ptr<EngineHandle> callback_ptr;
public:
FalaEngine();
~FalaEngine();
// Events
delegate void OnRecognizedDele(FalaAPI::RecoResult^ result);
property OnRecognizedDele^ OnRecognized;
delegate void OnEngineStartDele();
property OnEngineStartDele^ OnEngineStart;
private:
Recog *recog;
Jconf *jconf;
};
void StartOnEngineStart(Recog *recog, void * dummy)
{
FalaEngine^ that = static_cast<EngineHandle*>(dummy)->handle;
that->OnEngineStart(); // C++/CLI already checks if the invocation list is empty
}
void StartOnRecognized(Recog *recog, void * dummy)
{
FalaEngine^ that = static_cast<EngineHandle*>(dummy)->handle;
that->OnRecognized(recog->get_result());
}
FalaEngine::FalaEngine()
: callback_ptr(new EngineHandle(this))
{
recog = j_recog_new();
jconf = j_jconf_new();
//Julius callback Functions
callback_add(recog, CALLBACK_EVENT_PROCESS_ONLINE, StartOnEngineStart, callback_ptr.get());
callback_add(recog, CALLBACK_RESULT, StartOnRecognized, callback_ptr.get());
}
The clr_scoped_ptr class is here. There are not many license requirements, make sure you follow them though if you use it.