2963 lines
94 KiB
C++
2963 lines
94 KiB
C++
/* Copyright 2001-2016 The MathWorks, Inc. */
|
|
|
|
#ifndef _MCLCOMCLASS_H_
|
|
#define _MCLCOMCLASS_H_
|
|
|
|
#pragma warning( disable : 4786 )
|
|
#include "mclmcrrt.h"
|
|
#include "mwcomutil.h"
|
|
#include <olectl.h>
|
|
#include <wchar.h>
|
|
|
|
#ifdef __cplusplus
|
|
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <set>
|
|
#include <queue>
|
|
|
|
#ifndef HWND_MESSAGE
|
|
#define HWND_MESSAGE (HWND)NULL
|
|
#endif
|
|
|
|
// Structure used to pass supporting info for objects into CMCLModule Init method.
|
|
|
|
typedef struct _MCLOBJECT_MAP_ENTRY
|
|
{
|
|
// Pointer to CLSID value
|
|
|
|
const CLSID* pclsid;
|
|
// Pointer to function responsible for registering the class
|
|
|
|
HRESULT (__stdcall* pfnRegisterClass)(const GUID*, unsigned short, unsigned short,
|
|
const char*, const char*, const char*, const char*);
|
|
// Pointer to function responsible for unregistering the class
|
|
|
|
HRESULT (__stdcall* pfnUnregisterClass)(const char*, const char*);
|
|
// Pointer to function responsible for returning an instance of the object's class factory
|
|
|
|
HRESULT (__stdcall* pfnGetClassObject)(REFCLSID, REFIID, void**);
|
|
// Class's friendly name
|
|
|
|
const char* lpszFriendlyName;
|
|
// Class's version independent ProgID
|
|
|
|
const char* lpszVerIndProgID;
|
|
// Class's ProgID
|
|
|
|
const char* lpszProgID;
|
|
} _MCLOBJECT_MAP_ENTRY, *MCLOBJECT_MAP_ENTRY;
|
|
|
|
class mwLock
|
|
{
|
|
public:
|
|
mwLock()
|
|
{
|
|
mclAcquireMutex();
|
|
}
|
|
virtual ~mwLock()
|
|
{
|
|
mclReleaseMutex();
|
|
}
|
|
};
|
|
|
|
class IMCLFeval
|
|
{
|
|
public:
|
|
virtual bool Feval(HMCRINSTANCE hinst, const char* name, int nlhs, mxArray** plhs, int nrhs, mxArray** prhs) = 0;
|
|
virtual const char* getErrorMessage() const = 0;
|
|
virtual void RaiseEvent(OLECHAR* lpwszName, DISPID dispid, IDispatch* pDisp, DISPPARAMS* pDispParams) = 0;
|
|
virtual bool init(HINSTANCE hInstance) = 0;
|
|
virtual bool stop() = 0;
|
|
};
|
|
|
|
class IMCLEvent
|
|
{
|
|
public:
|
|
virtual void mclRaiseEventA(const char* lpszName, DISPID dispid, int nargin, mxArray** prhs) = 0;
|
|
};
|
|
|
|
class IMCLEventMap
|
|
{
|
|
public:
|
|
virtual int size() = 0;
|
|
virtual void add(void *context, IMCLEvent* pEvent) = 0;
|
|
virtual void remove(void *context, IMCLEvent* pEvent) = 0;
|
|
virtual void invokeA(void *context, const char* lpszName, DISPID dispid, int nargin, mxArray** prhs) = 0;
|
|
};
|
|
|
|
typedef bool (*MCLInitializeInstancePtr)(HMCRINSTANCE*, const char* path_to_component);
|
|
typedef bool (*MCLInitializeInstanceExPtr)(HMCRINSTANCE*, const char* path_to_component,
|
|
mclCtfStream ctfStream);
|
|
typedef bool (*MCLTerminateInstancePtr)(HMCRINSTANCE*);
|
|
|
|
#define WM_FEVALCOMPLETE WM_USER
|
|
#define WM_EVENTPENDING WM_USER+1
|
|
|
|
// Class for managing global list of event listeners.
|
|
class mclEventMap : public IMCLEventMap
|
|
{
|
|
public:
|
|
mclEventMap(){}
|
|
virtual ~mclEventMap(){}
|
|
// Returns current number of listeners
|
|
int size()
|
|
{
|
|
mwLock lock;
|
|
return (int)m_events.size();
|
|
}
|
|
// Adds a listener
|
|
void add(void *context, IMCLEvent* pEvent)
|
|
{
|
|
mwLock lock;
|
|
if (!pEvent)
|
|
return;
|
|
std::map<void *, IMCLEvent*>::iterator it = m_events.find(context);
|
|
if (it == m_events.end())
|
|
m_events[context] = pEvent;
|
|
}
|
|
// Removes a listener
|
|
void remove(void *context, IMCLEvent* pEvent)
|
|
{
|
|
(void)pEvent;
|
|
mwLock lock;
|
|
std::map<void *, IMCLEvent*>::iterator it = m_events.find(context);
|
|
if (it != m_events.end())
|
|
m_events.erase(it);
|
|
}
|
|
// Invokes the named event in the listener of current call context.
|
|
void invokeA(void *context, const char* lpszName, DISPID dispid, int nargin, mxArray** prhs)
|
|
{
|
|
mwLock lock;
|
|
std::map<void *, IMCLEvent*>::iterator it = m_events.find(context);
|
|
if (it != m_events.end())
|
|
{
|
|
((*it).second)->mclRaiseEventA(lpszName, dispid, nargin, prhs);
|
|
}
|
|
}
|
|
private:
|
|
std::map<void *, IMCLEvent*> m_events; // Array of listeners
|
|
};
|
|
|
|
// Class for managing global list of event listeners for singleton MCR case.
|
|
class mclSingleEventMap : public IMCLEventMap
|
|
{
|
|
public:
|
|
mclSingleEventMap(){}
|
|
virtual ~mclSingleEventMap(){}
|
|
// Returns current number of listeners
|
|
int size()
|
|
{
|
|
mwLock lock;
|
|
return (int)m_events.size();
|
|
}
|
|
// Adds a listener
|
|
void add(void *context, IMCLEvent* pEvent)
|
|
{
|
|
mwLock lock;
|
|
if (!pEvent)
|
|
return;
|
|
m_events.insert(pEvent);
|
|
}
|
|
// Removes a listener
|
|
void remove(void *context, IMCLEvent* pEvent)
|
|
{
|
|
context;
|
|
mwLock lock;
|
|
std::set<IMCLEvent*>::iterator it = m_events.find(pEvent);
|
|
if (it != m_events.end())
|
|
m_events.erase(it);
|
|
}
|
|
// Invokes the named event in the listener of current call context.
|
|
void invokeA(void *context, const char* lpszName, DISPID dispid, int nargin, mxArray** prhs)
|
|
{
|
|
context;
|
|
mwLock lock;
|
|
std::set<IMCLEvent*>::iterator it = m_events.begin();
|
|
while (it != m_events.end())
|
|
{
|
|
(*it)->mclRaiseEventA(lpszName, dispid, nargin, prhs);
|
|
it++;
|
|
}
|
|
}
|
|
private:
|
|
std::set<IMCLEvent*> m_events; // Array of listeners
|
|
};
|
|
|
|
// Stack proxy for a mutex
|
|
class ModuleLock
|
|
{
|
|
public:
|
|
ModuleLock()
|
|
{
|
|
#if defined (mclcommain_h) || defined (mclxlmain_published_api_h)
|
|
RequestGlobalLock();
|
|
#else
|
|
mclRequestGlobalLock();
|
|
#endif
|
|
}
|
|
virtual ~ModuleLock()
|
|
{
|
|
#if defined (mclcommain_h) || defined (mclxlmain_published_api_h)
|
|
ReleaseGlobalLock();
|
|
#else
|
|
mclReleaseGlobalLock();
|
|
#endif
|
|
}
|
|
private:
|
|
ModuleLock(const ModuleLock& ml);
|
|
ModuleLock& operator=(const ModuleLock& ml);
|
|
};
|
|
|
|
class mclSimpleFeval : public IMCLFeval
|
|
{
|
|
public:
|
|
mclSimpleFeval(){}
|
|
virtual ~mclSimpleFeval(){}
|
|
// Calls feval.
|
|
virtual bool Feval(HMCRINSTANCE hinst, const char* name, int nlhs, mxArray** plhs, int nrhs, mxArray** prhs)
|
|
{
|
|
return mclFeval(hinst, name, nlhs, plhs, nrhs, prhs);
|
|
}
|
|
// Makes an event callback.
|
|
virtual void RaiseEvent(OLECHAR* lpwszName, DISPID mdispid, IDispatch* pDisp, DISPPARAMS* pDispParams)
|
|
{
|
|
DISPID dispid = 0;
|
|
VARIANT varResult;
|
|
HRESULT hr = S_OK;
|
|
|
|
VariantInit(&varResult);
|
|
if (SUCCEEDED(hr = pDisp->GetIDsOfNames(IID_NULL, &lpwszName, 1, LOCALE_USER_DEFAULT, &dispid)))
|
|
{
|
|
hr = pDisp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, pDispParams,
|
|
&varResult, NULL, NULL);
|
|
}
|
|
}
|
|
virtual const char* getErrorMessage() const
|
|
{
|
|
return mclGetLastErrorMessage();
|
|
}
|
|
virtual bool init(HINSTANCE hInstance)
|
|
{
|
|
(void)hInstance;
|
|
return true;
|
|
}
|
|
virtual bool stop()
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
// Feval with events. Creates a thread on which all
|
|
// feval calls are made. Calling thread waits on feval
|
|
// thread to complete feval call. If an event arrives
|
|
// before the feval call returns, the calling thread
|
|
// executes the callback, then resumes waiting for the
|
|
// the feval to finish. This ensures that synchronous
|
|
// callbacks are always executed by the same thread that
|
|
// made the original feval call. This is needed because
|
|
// Visual Basic does not allow callbacks to be executed
|
|
// on a seperate thread, and the MCR always calls back on
|
|
// its thread, not the caller's.
|
|
class mclFevalWithEvents : public IMCLFeval
|
|
{
|
|
private:
|
|
// Class used to pass feval args when being called by
|
|
// Same thread as the message window runs on. When
|
|
// feval is complete, posts a message to the window.
|
|
class FevalArgs
|
|
{
|
|
public:
|
|
FevalArgs(HMCRINSTANCE hinst, const char* name, int nlhs, mxArray** plhs, int nrhs, mxArray** prhs, HWND hWnd)
|
|
: m_hinst(hinst), m_name(name), m_nlhs(nlhs), m_plhs(plhs), m_nrhs(nrhs), m_prhs(prhs), m_retval(false), m_hWnd(hWnd), m_errorMessage(""){}
|
|
virtual ~FevalArgs(){}
|
|
virtual bool execute()
|
|
{
|
|
m_retval = mclFeval(m_hinst, m_name, m_nlhs, m_plhs, m_nrhs, m_prhs);
|
|
|
|
if(!m_retval)
|
|
{
|
|
m_errorMessage = mclGetLastErrorMessage();
|
|
}
|
|
|
|
PostMessage(m_hWnd, WM_FEVALCOMPLETE, 0, 0);
|
|
return m_retval;
|
|
}
|
|
bool retval() const {return m_retval;}
|
|
const char* getErrorMessage() const {return m_errorMessage;}
|
|
private:
|
|
HMCRINSTANCE m_hinst;
|
|
const char* m_name;
|
|
int m_nlhs;
|
|
mxArray** m_plhs;
|
|
int m_nrhs;
|
|
mxArray** m_prhs;
|
|
bool m_retval;
|
|
HWND m_hWnd;
|
|
const char* m_errorMessage;
|
|
private:
|
|
FevalArgs();
|
|
};
|
|
// Class used to pass event args
|
|
class EventArgs
|
|
{
|
|
public:
|
|
EventArgs(OLECHAR* lpwszName, DISPID dispid, IDispatch* pDisp, DISPPARAMS* pDispParams)
|
|
: m_lpwszName(lpwszName), m_dispid(dispid), m_pDisp(pDisp), m_pDispParams(pDispParams), m_hr(S_OK)
|
|
{
|
|
VariantInit(&m_varResult);
|
|
}
|
|
virtual ~EventArgs()
|
|
{
|
|
VariantClear(&m_varResult);
|
|
}
|
|
virtual bool execute()
|
|
{
|
|
DISPID dispid = 0;
|
|
|
|
m_hr = m_pDisp->Invoke(m_dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, m_pDispParams,
|
|
&m_varResult, NULL, NULL);
|
|
return (SUCCEEDED(m_hr));
|
|
}
|
|
HRESULT retval() {return m_hr;}
|
|
private:
|
|
OLECHAR* m_lpwszName;
|
|
IDispatch* m_pDisp;
|
|
DISPPARAMS* m_pDispParams;
|
|
VARIANT m_varResult;
|
|
HRESULT m_hr;
|
|
DISPID m_dispid;
|
|
private:
|
|
EventArgs();
|
|
};
|
|
public:
|
|
mclFevalWithEvents() : m_hInstance(NULL), m_hThread(NULL), m_hWnd(NULL), m_errorMessage("") {}
|
|
virtual ~mclFevalWithEvents()
|
|
{
|
|
stop();
|
|
}
|
|
// Calls feval. Queues up the request, then
|
|
// waits for the call to finish.
|
|
virtual bool Feval(HMCRINSTANCE hinst, const char* name, int nlhs, mxArray** plhs, int nrhs, mxArray** prhs)
|
|
{
|
|
HWND hWnd = NULL;
|
|
BOOL bRet = FALSE;
|
|
MSG msg = {0, 0, 0, 0, 0, {0,0}};
|
|
if (!(hWnd = get_thread_window()))
|
|
return false;
|
|
FevalArgs Args(hinst, name, nlhs, plhs, nrhs, prhs, hWnd);
|
|
// Queue the request
|
|
add(&Args);
|
|
// Start timer to periodically refresh windows owned by this thread
|
|
SetTimer(hWnd, 1, 100, NULL);
|
|
// Process window message loop. Break when feval is finished.
|
|
while ((bRet = GetMessage(&msg, hWnd, 0, 0)) != 0)
|
|
{
|
|
if (bRet == -1)
|
|
{
|
|
return false;
|
|
}
|
|
else if (msg.message == WM_FEVALCOMPLETE)
|
|
{
|
|
break;
|
|
}
|
|
else if (msg.message == WM_TIMER)
|
|
{
|
|
DoEvents();
|
|
}
|
|
else
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
// Stop timer
|
|
KillTimer(hWnd, 1);
|
|
m_errorMessage = Args.getErrorMessage();
|
|
return Args.retval();
|
|
}
|
|
|
|
virtual const char* getErrorMessage() const
|
|
{
|
|
return m_errorMessage;
|
|
}
|
|
// Makes an event callback. Queues up the call,
|
|
// then waits for it to finish.
|
|
virtual void RaiseEvent(OLECHAR* lpwszName, DISPID dispid, IDispatch* pDisp, DISPPARAMS* pDispParams)
|
|
{
|
|
EventArgs Args(lpwszName, dispid, pDisp, pDispParams);
|
|
SendMessage(m_hWnd, WM_EVENTPENDING, 0, (LPARAM)(&Args));
|
|
}
|
|
// Performs idle processing.
|
|
void DoEvents()
|
|
{
|
|
EnumThreadWindows(GetCurrentThreadId(), EnumThreadWndProc, NULL);
|
|
}
|
|
// Starts the feval thread
|
|
virtual bool init(HINSTANCE hInstance)
|
|
{
|
|
m_hInstance = hInstance;
|
|
if (!(m_hExit = CreateEvent(NULL, FALSE, FALSE, NULL)))
|
|
return false;
|
|
if (!(m_hFevalPending = CreateEvent(NULL, FALSE, FALSE, NULL)))
|
|
return false;
|
|
WNDCLASS wc = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
if (!GetClassInfo(m_hInstance, "feval_window_class", &wc))
|
|
{
|
|
WNDCLASS wc_new = {0, (WNDPROC)ModuleWndProc, 0, 0, m_hInstance, NULL, NULL, NULL, NULL, "feval_window_class"};
|
|
if (!RegisterClass(&wc_new))
|
|
return false;
|
|
}
|
|
if (!(m_hWnd = get_thread_window()))
|
|
return false;
|
|
if (!(m_hThread = CreateThread(NULL, 0, FevalProc, (LPVOID)this, 0, &m_dwThreadID)))
|
|
return false;
|
|
return true;
|
|
}
|
|
// Stops the feval thread, destroys window
|
|
virtual bool stop()
|
|
{
|
|
SetEvent(m_hExit);
|
|
WaitForSingleObject(m_hThread, INFINITE);
|
|
CloseHandle(m_hThread);
|
|
std::map<DWORD, HWND>::iterator it = m_ThreadData.begin();
|
|
while(it != m_ThreadData.end())
|
|
{
|
|
HWND hWnd = (*it).second;
|
|
if (hWnd)
|
|
DestroyWindow(hWnd);
|
|
it++;
|
|
}
|
|
UnregisterClass("feval_window_class", m_hInstance);
|
|
m_hWnd = NULL;
|
|
m_ThreadData.clear();
|
|
CloseHandle(m_hExit);
|
|
CloseHandle(m_hFevalPending);
|
|
return true;
|
|
}
|
|
private:
|
|
// Returns the HWND associated with the current thread
|
|
// A new HWND is created by this function the first time
|
|
// it gets called on a given thread.
|
|
HWND get_thread_window()
|
|
{
|
|
ModuleLock lock;
|
|
DWORD dwThreadID = GetCurrentThreadId();
|
|
HWND hWnd = NULL;
|
|
std::map<DWORD, HWND>::iterator it = m_ThreadData.find(dwThreadID);
|
|
if (it == m_ThreadData.end())
|
|
{
|
|
char tmp[128];
|
|
char window_name[128];
|
|
sprintf(tmp, "%u", dwThreadID);
|
|
strcpy(window_name, "feval_");
|
|
strcat(window_name, tmp);
|
|
strcat(window_name, "_window");
|
|
hWnd = CreateWindowEx(0, "feval_window_class", window_name, 0,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
HWND_MESSAGE, (HMENU)NULL, m_hInstance, NULL);
|
|
if (!hWnd)
|
|
{
|
|
return NULL;
|
|
}
|
|
m_ThreadData[dwThreadID] = hWnd;
|
|
}
|
|
else
|
|
hWnd = (*it).second;
|
|
return hWnd;
|
|
}
|
|
// Adds an feval call
|
|
void add(FevalArgs* pArgs)
|
|
{
|
|
ModuleLock lock;
|
|
m_fevalQueue.push(pArgs);
|
|
SetEvent(m_hFevalPending);
|
|
}
|
|
// Gets the next available feval call
|
|
FevalArgs* get_next()
|
|
{
|
|
ModuleLock lock;
|
|
if (m_fevalQueue.size() == 0)
|
|
return NULL;
|
|
FevalArgs* pArgs = m_fevalQueue.front();
|
|
m_fevalQueue.pop();
|
|
return pArgs;
|
|
}
|
|
// Feval thread function. Executes fevals as they become available,
|
|
// Exits when signaled to.
|
|
static DWORD WINAPI FevalProc(LPVOID pThis)
|
|
{
|
|
mclFevalWithEvents* pf = static_cast<mclFevalWithEvents*>(pThis);
|
|
HANDLE hEvents[2] = {pf->m_hFevalPending, pf->m_hExit};
|
|
DWORD dwWait = 0;
|
|
|
|
for (;;)
|
|
{
|
|
dwWait = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
|
|
if (dwWait == WAIT_OBJECT_0)
|
|
{
|
|
FevalArgs* pArgs = NULL;
|
|
while ((pArgs = pf->get_next()))
|
|
pArgs->execute();
|
|
}
|
|
else if (dwWait == WAIT_OBJECT_0+1)
|
|
break;
|
|
else
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
// Window process for HWNDs created by this class.
|
|
static LRESULT CALLBACK ModuleWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_EVENTPENDING:
|
|
{
|
|
EventArgs* pArgs = (EventArgs*)lParam;
|
|
if (pArgs)
|
|
pArgs->execute();
|
|
break;
|
|
}
|
|
default:
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|
|
// This function clears all WM_PAINT messages from the window
|
|
static BOOL CALLBACK EnumThreadWndProc(HWND hWnd, LPARAM lParam)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
MSG msg = {0, 0, 0, 0, 0, {0,0}};
|
|
while((bRet = PeekMessage(&msg, hWnd, WM_PAINT, WM_PAINT, PM_REMOVE)))
|
|
{
|
|
if (bRet == -1)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
private:
|
|
HINSTANCE m_hInstance; // HINSTANCE passed from DLLMain
|
|
HANDLE m_hThread;
|
|
DWORD m_dwThreadID;
|
|
HANDLE m_hExit;
|
|
HANDLE m_hFevalPending;
|
|
std::queue<FevalArgs*> m_fevalQueue;
|
|
HWND m_hWnd;
|
|
std::map<DWORD, HWND> m_ThreadData;
|
|
const char* m_errorMessage;
|
|
};
|
|
/*------------------------------------------------------------------------------
|
|
CMCLModule class definition. The CMCLModule class is used as the global
|
|
DLL controller object for all COM DLL's. Manages global lock count,
|
|
registration/de-registration, and class factory services for the DLL.
|
|
------------------------------------------------------------------------------*/
|
|
class CMCLModule
|
|
{
|
|
/*--------------------------------------
|
|
Construction/destruction
|
|
--------------------------------------*/
|
|
public:
|
|
CMCLModule(bool with_events = false)
|
|
{
|
|
InitializeData(NULL, with_events);
|
|
m_pEvents = new mclEventMap();
|
|
}
|
|
CMCLModule(IMCLEventMap* pEvents, bool with_events = false)
|
|
{
|
|
InitializeData(NULL, with_events);
|
|
m_pEvents = pEvents;
|
|
}
|
|
CMCLModule(MCLInitializeInstancePtr pInit, MCLTerminateInstancePtr pTerm, bool with_events = false)
|
|
{
|
|
InitializeData(pTerm, with_events);
|
|
m_pInitialize = pInit;
|
|
m_pEvents = new mclEventMap();
|
|
}
|
|
CMCLModule(MCLInitializeInstancePtr pInit, MCLTerminateInstancePtr pTerm, IMCLEventMap* pEvents, bool with_events = false)
|
|
{
|
|
InitializeData(pTerm, with_events);
|
|
m_pInitialize = pInit;
|
|
m_pEvents = pEvents;
|
|
}
|
|
|
|
CMCLModule(MCLInitializeInstanceExPtr pInit, MCLTerminateInstancePtr pTerm, bool with_events = false)
|
|
{
|
|
InitializeData(pTerm, with_events);
|
|
m_pInitializeEx = pInit;
|
|
m_pEvents = new mclEventMap();
|
|
}
|
|
CMCLModule(MCLInitializeInstanceExPtr pInit, MCLTerminateInstancePtr pTerm, IMCLEventMap* pEvents, bool with_events = false)
|
|
{
|
|
InitializeData(pTerm, with_events);
|
|
m_pInitializeEx = pInit;
|
|
m_pEvents = pEvents;
|
|
}
|
|
|
|
virtual ~CMCLModule()
|
|
{
|
|
if (m_pFeval)
|
|
delete m_pFeval;
|
|
if (m_pEvents)
|
|
delete m_pEvents;
|
|
}
|
|
/*--------------------------------------
|
|
Methods
|
|
--------------------------------------*/
|
|
public:
|
|
// Initialization/uninitialization method to be called from DLLMain
|
|
virtual BOOL InitMain(MCLOBJECT_MAP_ENTRY pobjectmap, const GUID* plibid, WORD wMajor, WORD wMinor,
|
|
HINSTANCE hInstance, DWORD dwReason, void* pv)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (dwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
char szDllPath[_MAX_PATH];
|
|
char szDir[_MAX_DIR];
|
|
if (!Init(pobjectmap, hInstance, plibid, wMajor, wMinor))
|
|
return FALSE;
|
|
DisableThreadLibraryCalls(hInstance);
|
|
if (GetModuleFileName(hInstance, szDllPath, _MAX_PATH) > 0)
|
|
{
|
|
_splitpath(szDllPath, m_szPath, szDir, NULL, NULL);
|
|
strcat(m_szPath, szDir);
|
|
}
|
|
}
|
|
else if (dwReason == DLL_PROCESS_DETACH)
|
|
{
|
|
Term();
|
|
}
|
|
return TRUE;
|
|
}
|
|
// Initializes the class with object, instance, and type lib info
|
|
virtual bool Init(MCLOBJECT_MAP_ENTRY pobjectmap, HINSTANCE hInstance, const GUID* plibid, WORD wMajor, WORD wMinor)
|
|
{
|
|
if (isInitialized())
|
|
return true;
|
|
m_hInstance = hInstance;
|
|
m_plibid = plibid;
|
|
m_wMajor = wMajor;
|
|
m_wMinor = wMinor;
|
|
m_pObjectMap = pobjectmap;
|
|
if (!m_pFeval->init(hInstance))
|
|
return false;
|
|
m_bInitialized = TRUE;
|
|
m_bCompatibilitySet = FALSE;
|
|
return true;
|
|
}
|
|
// Uninitializes the class
|
|
virtual void Term()
|
|
{
|
|
if (!isInitialized())
|
|
return;
|
|
m_hInstance = NULL;
|
|
m_plibid = NULL;
|
|
m_wMajor = 0;
|
|
m_wMinor = 0;
|
|
m_bInitialized = FALSE;
|
|
}
|
|
// Returns the current lock count
|
|
long GetLockCount()
|
|
{
|
|
long cCount = 0;
|
|
InterlockedExchange(&cCount, m_cLockCount);
|
|
return cCount;
|
|
}
|
|
// Updates the registry for all classes in list and type lib. TRUE = Register, FALSE = Unregister
|
|
virtual HRESULT UpdateRegistry(BOOL bRegister)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!isInitialized())
|
|
return E_FAIL;
|
|
if (bRegister)
|
|
{
|
|
char szDllPath[MAX_PATH];
|
|
OLECHAR *wDllPath=NULL;
|
|
ITypeLib* pTypeLib = 0;
|
|
|
|
GetModuleFileName(m_hInstance, szDllPath, MAX_PATH);
|
|
try
|
|
{
|
|
int wLength = (int)strlen (szDllPath) + 1; // +1 means don't forget the terminating null character
|
|
wDllPath = new OLECHAR[wLength];
|
|
MultiByteToWideChar (
|
|
CP_ACP, // code page
|
|
MB_PRECOMPOSED, // character-type options
|
|
szDllPath, // address of string to map
|
|
wLength, // number of characters in string
|
|
wDllPath, // address of wide-character buffer
|
|
wLength); // size of buffer
|
|
hr = LoadTypeLibEx(wDllPath, REGKIND_REGISTER, &pTypeLib);
|
|
if(FAILED(hr)) {
|
|
delete wDllPath;
|
|
wDllPath = NULL;
|
|
return hr;
|
|
}
|
|
pTypeLib->Release();
|
|
if (m_pObjectMap == NULL)
|
|
return S_OK;
|
|
int i = 0;
|
|
while (m_pObjectMap[i].pclsid != NULL)
|
|
{
|
|
hr = m_pObjectMap[i].pfnRegisterClass(m_plibid, m_wMajor, m_wMinor, m_pObjectMap[i].lpszFriendlyName,
|
|
m_pObjectMap[i].lpszVerIndProgID, m_pObjectMap[i].lpszProgID, szDllPath);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
i++;
|
|
}
|
|
|
|
delete wDllPath;
|
|
wDllPath = NULL;
|
|
}
|
|
catch(...) {
|
|
if(wDllPath != NULL) {
|
|
delete wDllPath;
|
|
wDllPath = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef _WIN64
|
|
hr = UnRegisterTypeLib(*m_plibid, m_wMajor, m_wMinor, LANG_NEUTRAL, SYS_WIN64);
|
|
//since we add a "win32" key under typelib at the time of registration,
|
|
//we need to remove them here.
|
|
hr = UnRegisterTypeLib(*m_plibid, m_wMajor, m_wMinor, LANG_NEUTRAL, SYS_WIN32);
|
|
#else
|
|
hr = UnRegisterTypeLib(*m_plibid, m_wMajor, m_wMinor, LANG_NEUTRAL, SYS_WIN32);
|
|
#endif
|
|
int i = 0;
|
|
while (m_pObjectMap[i].pclsid != NULL)
|
|
{
|
|
hr = m_pObjectMap[i].pfnUnregisterClass(m_pObjectMap[i].lpszVerIndProgID, m_pObjectMap[i].lpszProgID);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
i++;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
// Returns a class factory pointer for the specified CLSID
|
|
virtual HRESULT GetClassObject(REFCLSID clsid, REFIID iid, void** ppv)
|
|
{
|
|
if (!mclmcrInitialize2(mclStandaloneContainer))
|
|
return CLASS_E_CLASSNOTAVAILABLE;
|
|
|
|
//COM application should be run in compatibility mode
|
|
if(!m_bCompatibilitySet)
|
|
{
|
|
mclSetInterleavedCompatibility(true);
|
|
m_bCompatibilitySet = TRUE;
|
|
}
|
|
|
|
if (!isInitialized())
|
|
return CLASS_E_CLASSNOTAVAILABLE;
|
|
if (m_pObjectMap == NULL)
|
|
return CLASS_E_CLASSNOTAVAILABLE;
|
|
int i = 0;
|
|
while (m_pObjectMap[i].pclsid != NULL)
|
|
{
|
|
if (*(m_pObjectMap[i].pclsid) == clsid)
|
|
return m_pObjectMap[i].pfnGetClassObject(clsid, iid, ppv);
|
|
i++;
|
|
}
|
|
return CLASS_E_CLASSNOTAVAILABLE;
|
|
}
|
|
// Increments the lock count
|
|
long Lock() {return InterlockedIncrement(&m_cLockCount);}
|
|
// Decrements the lock count
|
|
long Unlock() {return InterlockedDecrement(&m_cLockCount);}
|
|
// Returns a new type info pointer for the module's type lib
|
|
HRESULT GetTypeInfo(REFGUID riid, ITypeInfo** ppTypeInfo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ITypeLib* pTypeLib = NULL;
|
|
|
|
if (!isInitialized())
|
|
return E_FAIL;
|
|
if(FAILED(hr = LoadRegTypeLib(*m_plibid, m_wMajor, m_wMinor, LANG_NEUTRAL, &pTypeLib)))
|
|
return hr;
|
|
hr = pTypeLib->GetTypeInfoOfGuid(riid, ppTypeInfo);
|
|
pTypeLib->Release();
|
|
return hr;
|
|
}
|
|
// Returns the progid for a given clsid, returns null if invalid clsid
|
|
const char* GetProgID(REFCLSID clsid)
|
|
{
|
|
int i = 0;
|
|
while (m_pObjectMap[i].pclsid != NULL)
|
|
{
|
|
if (*(m_pObjectMap[i].pclsid) == clsid)
|
|
return m_pObjectMap[i].lpszProgID;
|
|
i++;
|
|
}
|
|
return NULL;
|
|
}
|
|
// Initializes the MCR instance. Called by object constructors
|
|
virtual bool InitializeComponentInstance(HMCRINSTANCE* inst)
|
|
{
|
|
if (!m_pInitialize || !inst)
|
|
return false;
|
|
return m_pInitialize(inst, getPath());
|
|
}
|
|
// Initializes the MCR instance with CTF embedded in resource file.
|
|
// Called by object constructors
|
|
virtual bool InitializeComponentInstanceEx(HMCRINSTANCE* inst)
|
|
{
|
|
if (!m_pInitializeEx || !inst)
|
|
return false;
|
|
|
|
DWORD ctfSize = 0;
|
|
char* ctfData = GetEmbeddedCtf(ctfSize);
|
|
if(ctfData == NULL || ctfSize <= 0)
|
|
return false;
|
|
|
|
mclCtfStream ctfStream = mclGetStreamFromArraySrc(ctfData, ctfSize);
|
|
bool bResult = m_pInitializeEx(inst, getPath(), ctfStream);
|
|
|
|
if(ctfStream != NULL)
|
|
{
|
|
mclDestroyStream(ctfStream);
|
|
}
|
|
return bResult;
|
|
}
|
|
// Terminates an MCR instance. Called by object destructors.
|
|
virtual bool TerminateInstance(HMCRINSTANCE* inst)
|
|
{
|
|
if (!m_pTerminate || !inst)
|
|
return false;
|
|
return m_pTerminate(inst);
|
|
}
|
|
virtual BOOL isInitialized()
|
|
{
|
|
return m_bInitialized;
|
|
}
|
|
virtual void setInitialized(BOOL bInit)
|
|
{
|
|
m_bInitialized = bInit;
|
|
}
|
|
// Returns the path to the Dll
|
|
const char* getPath()
|
|
{
|
|
return m_szPath;
|
|
}
|
|
// Returns a reference to the Event map
|
|
virtual IMCLEventMap* getEventMap()
|
|
{
|
|
return m_pEvents;
|
|
}
|
|
virtual bool Feval(HMCRINSTANCE hinst, const char* name, int nlhs, mxArray** plhs, int nrhs, mxArray** prhs)
|
|
{
|
|
return m_pFeval->Feval(hinst, name, nlhs, plhs, nrhs, prhs);
|
|
}
|
|
virtual const char* getErrorMessage()
|
|
{
|
|
return m_pFeval->getErrorMessage();
|
|
}
|
|
virtual void RaiseEvent(OLECHAR* lpwszName, long dispid, IDispatch* pDisp, DISPPARAMS* pDispParams)
|
|
{
|
|
m_pFeval->RaiseEvent(lpwszName, dispid, pDisp, pDispParams);
|
|
}
|
|
|
|
char* GetEmbeddedCtf(DWORD& ctfSize)
|
|
{
|
|
DWORD err = 0;
|
|
ctfSize = 0;
|
|
|
|
HRSRC rsrc = FindResource(m_hInstance, MAKEINTRESOURCE(2),"RT_RCDATA");
|
|
if(!rsrc)
|
|
{
|
|
err = GetLastError();
|
|
return NULL;
|
|
}
|
|
|
|
ctfSize = SizeofResource(m_hInstance, rsrc);
|
|
HGLOBAL MemoryHandle = LoadResource(m_hInstance, rsrc);
|
|
if(MemoryHandle == NULL)
|
|
{
|
|
err = GetLastError();
|
|
return NULL;
|
|
}
|
|
char *ctfData = (char *) LockResource(MemoryHandle);
|
|
if(ctfData == NULL)
|
|
{
|
|
err = GetLastError();
|
|
}
|
|
|
|
return ctfData;
|
|
}
|
|
|
|
private:
|
|
void InitializeData(MCLTerminateInstancePtr pTerm, bool with_events)
|
|
{
|
|
m_cLockCount = 0;
|
|
m_hInstance = NULL;
|
|
m_pObjectMap = NULL;
|
|
m_plibid = NULL;
|
|
m_wMajor = 0;
|
|
m_wMinor = 0;
|
|
m_bInitialized = FALSE;
|
|
m_szPath[0] = '\0';
|
|
m_pTerminate = pTerm;
|
|
m_pFeval = (with_events ? static_cast<IMCLFeval*>(new mclFevalWithEvents())
|
|
: static_cast<IMCLFeval*>(new mclSimpleFeval()));
|
|
}
|
|
|
|
/*--------------------------------------
|
|
Properties
|
|
--------------------------------------*/
|
|
protected:
|
|
MCLInitializeInstancePtr m_pInitialize; // Function used to create an MCR instance
|
|
MCLInitializeInstanceExPtr m_pInitializeEx; // Function used to create an MCR instance with embedded ctf
|
|
MCLTerminateInstancePtr m_pTerminate; // Function to destroy an MCR instance
|
|
private:
|
|
long m_cLockCount; // Lock count on module
|
|
HINSTANCE m_hInstance; // HINSTANCE passed from DLLMain
|
|
MCLOBJECT_MAP_ENTRY m_pObjectMap; // Object info array
|
|
const GUID* m_plibid; // LIBID of type lib
|
|
BOOL m_bInitialized; // Is-initialized flag
|
|
BOOL m_bCompatibilitySet; // Set-compatibility mode flag
|
|
WORD m_wMajor; // Major rev number of type lib
|
|
WORD m_wMinor; // Minor rev number of type lib
|
|
char m_szPath[_MAX_PATH]; // Stores the location of the Dll
|
|
IMCLEventMap* m_pEvents; // event map
|
|
IMCLFeval* m_pFeval;
|
|
};
|
|
|
|
/*------------------------------------------------------------------------------
|
|
CMCLSingleModule class definition. The CMCLSingleModule class specializes
|
|
the CMCLModule class for the case of a singleton MCR instance.
|
|
------------------------------------------------------------------------------*/
|
|
class CMCLSingleModule : public CMCLModule
|
|
{
|
|
/*--------------------------------------
|
|
Construction/destruction
|
|
--------------------------------------*/
|
|
public:
|
|
CMCLSingleModule(bool with_events = false) : CMCLModule(new mclSingleEventMap(), with_events), m_mcrInstance(NULL)
|
|
{
|
|
}
|
|
CMCLSingleModule(MCLInitializeInstancePtr pInit, MCLTerminateInstancePtr pTerm, bool with_events = false)
|
|
: CMCLModule(pInit, pTerm, new mclSingleEventMap(), with_events), m_mcrInstance(NULL)
|
|
{
|
|
}
|
|
CMCLSingleModule(MCLInitializeInstanceExPtr pInit, MCLTerminateInstancePtr pTerm, bool with_events = false)
|
|
: CMCLModule(pInit, pTerm, new mclSingleEventMap(), with_events), m_mcrInstance(NULL)
|
|
{
|
|
}
|
|
virtual ~CMCLSingleModule()
|
|
{
|
|
}
|
|
/*--------------------------------------
|
|
Methods
|
|
--------------------------------------*/
|
|
public:
|
|
// Uninitializes the class
|
|
void Term()
|
|
{
|
|
if (!isInitialized())
|
|
return;
|
|
CMCLModule::Term();
|
|
ModuleLock lock;
|
|
if (m_mcrInstance)
|
|
m_mcrInstance = NULL;
|
|
}
|
|
// Initializes the MCR instance. Called by object constructors
|
|
bool InitializeComponentInstance(HMCRINSTANCE* inst)
|
|
{
|
|
if (!inst)
|
|
return false;
|
|
bool ret = false;
|
|
ModuleLock lock;
|
|
if (!m_mcrInstance)
|
|
{
|
|
if (!m_pInitialize)
|
|
return false;
|
|
ret = m_pInitialize(&m_mcrInstance, getPath());
|
|
}
|
|
*inst = m_mcrInstance;
|
|
return ret;
|
|
}
|
|
// Initializes the MCR instance with CTF embedded in resource file.
|
|
// Called by object constructors
|
|
bool InitializeComponentInstanceEx(HMCRINSTANCE* inst)
|
|
{
|
|
if (!inst)
|
|
return false;
|
|
bool ret = false;
|
|
ModuleLock lock;
|
|
if (!m_mcrInstance)
|
|
{
|
|
if (!m_pInitializeEx)
|
|
return false;
|
|
|
|
DWORD ctfSize = 0;
|
|
char* ctfData = GetEmbeddedCtf(ctfSize);
|
|
if(ctfData == NULL || ctfSize <= 0)
|
|
return false;
|
|
|
|
mclCtfStream ctfStream = mclGetStreamFromArraySrc(ctfData, ctfSize);
|
|
ret = m_pInitializeEx(&m_mcrInstance, getPath(), ctfStream);
|
|
|
|
if(ctfStream != NULL)
|
|
{
|
|
mclDestroyStream(ctfStream);
|
|
}
|
|
}
|
|
*inst = m_mcrInstance;
|
|
return ret;
|
|
}
|
|
// Terminates an MCR instance. Called by object destructors.
|
|
bool TerminateInstance(HMCRINSTANCE* inst)
|
|
{
|
|
if (!inst)
|
|
return false;
|
|
*inst = NULL;
|
|
return true;
|
|
}
|
|
/*--------------------------------------
|
|
Properties
|
|
--------------------------------------*/
|
|
private:
|
|
HMCRINSTANCE m_mcrInstance; // Module-level MCR instance
|
|
};
|
|
|
|
class mclmxarray_list {
|
|
int count;
|
|
mxArray **list;
|
|
public:
|
|
mclmxarray_list( int icount, mxArray **ilist ) : count(icount), list(ilist) { }
|
|
~mclmxarray_list( ) {
|
|
for (int i =0; i<count; i++) {
|
|
mxDestroyArray( list[i] );
|
|
}
|
|
}
|
|
};
|
|
|
|
class CMCLEnumConnectionPoints : public IEnumConnectionPoints
|
|
{
|
|
public:
|
|
/*--------------------------------------
|
|
Construction/destruction
|
|
--------------------------------------*/
|
|
// CMCLEnumConnectionPoints default constructor
|
|
CMCLEnumConnectionPoints()
|
|
{
|
|
m_cRef = 1;
|
|
m_nIndex = 0;
|
|
m_pThis = NULL;
|
|
m_rgpcn = NULL;
|
|
m_cConnections = 0;
|
|
}
|
|
// CMCLEnumConnectionPoints constructor from container reference and array of connection points
|
|
CMCLEnumConnectionPoints(IUnknown* pThis, ULONG cConnections, IConnectionPoint** rgpcn)
|
|
{
|
|
m_cRef = 1;
|
|
m_nIndex = 0;
|
|
m_pThis = pThis;
|
|
m_rgpcn = NULL;
|
|
m_cConnections = 0;
|
|
if (cConnections > 0 && rgpcn != NULL)
|
|
{
|
|
m_cConnections = cConnections;
|
|
m_rgpcn = new IConnectionPoint*[m_cConnections];
|
|
if (m_rgpcn != NULL)
|
|
{
|
|
for(ULONG i=0;i<cConnections;i++)
|
|
{
|
|
m_rgpcn[i] = NULL;
|
|
if (rgpcn[i] != NULL)
|
|
rgpcn[i]->QueryInterface(IID_IConnectionPoint, (void**)&m_rgpcn[i]);
|
|
}
|
|
}
|
|
else
|
|
m_cConnections = 0;
|
|
}
|
|
}
|
|
// CMCLEnumConnectionPoints destructor
|
|
virtual ~CMCLEnumConnectionPoints()
|
|
{
|
|
if (m_rgpcn != NULL)
|
|
{
|
|
for (size_t i=0;i<m_cConnections;i++)
|
|
{
|
|
if (m_rgpcn[i] != NULL)
|
|
m_rgpcn[i]->Release();
|
|
}
|
|
delete [] m_rgpcn;
|
|
}
|
|
}
|
|
/*--------------------------------------
|
|
IUnknown implementation
|
|
--------------------------------------*/
|
|
public:
|
|
// IUnknown::AddRef implementation
|
|
ULONG __stdcall AddRef()
|
|
{
|
|
if (m_pThis != NULL)
|
|
m_pThis->AddRef();
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
// IUnknown::Release implementation
|
|
ULONG __stdcall Release()
|
|
{
|
|
if (m_pThis != NULL)
|
|
m_pThis->Release();
|
|
ULONG cRef = InterlockedDecrement(&m_cRef);
|
|
if(cRef != 0)
|
|
return cRef;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
// IUnknown::QueryInterface implementation
|
|
HRESULT __stdcall QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
if(riid == IID_IEnumConnectionPoints)
|
|
*ppv = static_cast<IEnumConnectionPoints*>(this);
|
|
else if(riid == IID_IUnknown)
|
|
*ppv = static_cast<IUnknown*>(this);
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
/*--------------------------------------
|
|
IEnumConnectionPoints implementation
|
|
--------------------------------------*/
|
|
// IEnumConnectionPoints::Next implementation
|
|
HRESULT __stdcall Next(ULONG cConnections, IConnectionPoint** rgpcn, ULONG* pcFetched)
|
|
{
|
|
LONG nIndex = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
if(rgpcn == NULL || pcFetched == NULL)
|
|
return E_INVALIDARG;
|
|
*pcFetched = 0;
|
|
if (cConnections == 0)
|
|
return S_OK;
|
|
for (ULONG i=0;i<cConnections;i++)
|
|
{
|
|
InterlockedExchange(&nIndex, m_nIndex);
|
|
if (nIndex >= (LONG)m_cConnections)
|
|
{
|
|
hr = S_FALSE;
|
|
break;
|
|
}
|
|
rgpcn[i] = m_rgpcn[nIndex];
|
|
if(rgpcn[i] != NULL)
|
|
rgpcn[i]->AddRef();
|
|
(*pcFetched)++;
|
|
InterlockedIncrement(&m_nIndex);
|
|
}
|
|
return hr;
|
|
}
|
|
// IEnumConnectionPoints::Skip implementation
|
|
HRESULT __stdcall Skip(ULONG cConnections)
|
|
{
|
|
LONG nIndex = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
InterlockedExchange(&nIndex, m_nIndex);
|
|
if (nIndex >= (LONG)m_cConnections)
|
|
return S_FALSE;
|
|
for (ULONG i=0;i<cConnections;i++)
|
|
{
|
|
nIndex = InterlockedIncrement(&m_nIndex);
|
|
if (nIndex >= (LONG)m_cConnections)
|
|
{
|
|
hr = S_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
// IEnumConnectionPoints::Reset implementation
|
|
HRESULT __stdcall Reset()
|
|
{
|
|
InterlockedExchange(&m_nIndex, 0);
|
|
return S_OK;
|
|
}
|
|
// IEnumConnectionPoints::Clone implementation
|
|
HRESULT __stdcall Clone(IEnumConnectionPoints** ppEnum)
|
|
{
|
|
CMCLEnumConnectionPoints* pNew = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
if(ppEnum == NULL)
|
|
return E_INVALIDARG;
|
|
*ppEnum = NULL;
|
|
pNew = new CMCLEnumConnectionPoints(m_pThis, (ULONG)m_cConnections, m_rgpcn);
|
|
if(pNew == NULL)
|
|
return E_OUTOFMEMORY;
|
|
pNew->m_nIndex = m_nIndex;
|
|
hr = pNew->QueryInterface(IID_IEnumConnectionPoints, (void**)ppEnum);
|
|
pNew->Release();
|
|
return hr;
|
|
}
|
|
/*--------------------------------------
|
|
CMCLEnumConnectionPoints Properties
|
|
--------------------------------------*/
|
|
private:
|
|
long m_cRef;
|
|
IUnknown* m_pThis; // Containing IUnknown for ref counting
|
|
long m_nIndex; // Index of current element
|
|
IConnectionPoint** m_rgpcn; // Array of connection points
|
|
ULONG m_cConnections; // Number of connection points in the array
|
|
};
|
|
|
|
class CMCLEnumConnections : public IEnumConnections
|
|
{
|
|
public:
|
|
/*--------------------------------------
|
|
Construction/destruction
|
|
--------------------------------------*/
|
|
// CMCLEnumConnections default constructor
|
|
CMCLEnumConnections()
|
|
{
|
|
m_cRef = 1;
|
|
m_nIndex = 0;
|
|
m_pThis = NULL;
|
|
m_rgpcd = NULL;
|
|
m_cConnections = 0;
|
|
}
|
|
// CMCLEnumConnections constructor from parent connection point reference and array of CONNECTDATA's
|
|
CMCLEnumConnections(IUnknown* pThis, ULONG cConnections, CONNECTDATA* rgpcd)
|
|
{
|
|
m_cRef = 1;
|
|
m_nIndex = 0;
|
|
m_pThis = pThis;
|
|
m_rgpcd = NULL;
|
|
m_cConnections = 0;
|
|
if (cConnections > 0 && rgpcd != NULL)
|
|
{
|
|
m_cConnections = cConnections;
|
|
m_rgpcd = new CONNECTDATA[m_cConnections];
|
|
if (m_rgpcd != NULL)
|
|
{
|
|
for(ULONG i=0;i<cConnections;i++)
|
|
{
|
|
m_rgpcd[i] = rgpcd[i];
|
|
if (m_rgpcd[i].pUnk != NULL)
|
|
m_rgpcd[i].pUnk->AddRef();
|
|
}
|
|
}
|
|
else
|
|
m_cConnections = 0;
|
|
}
|
|
}
|
|
// CMCLEnumConnections constructor from parent connection point reference and vector class of CONNECTDATA's
|
|
CMCLEnumConnections(IUnknown* pThis, std::vector<CONNECTDATA*>& vecpcd)
|
|
{
|
|
std::vector<CONNECTDATA*>::iterator it;
|
|
CONNECTDATA* pConnData = NULL;
|
|
int i = 0;
|
|
|
|
m_cRef = 1;
|
|
m_nIndex = 0;
|
|
m_pThis = pThis;
|
|
m_rgpcd = NULL;
|
|
m_cConnections = 0;
|
|
if (vecpcd.size() > 0)
|
|
{
|
|
m_cConnections = (ULONG)vecpcd.size();
|
|
m_rgpcd = new CONNECTDATA[m_cConnections];
|
|
if (m_rgpcd != NULL)
|
|
{
|
|
for(it = vecpcd.begin(); it != vecpcd.end(); it++)
|
|
{
|
|
pConnData = *it;
|
|
if (pConnData != NULL)
|
|
{
|
|
m_rgpcd[i] = *pConnData;
|
|
if (m_rgpcd[i].pUnk != NULL)
|
|
m_rgpcd[i].pUnk->AddRef();
|
|
}
|
|
else
|
|
{
|
|
m_rgpcd[i].pUnk = NULL;
|
|
m_rgpcd[i].dwCookie = 0;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
else
|
|
m_cConnections = 0;
|
|
}
|
|
}
|
|
// CMCLEnumConnections destructor
|
|
virtual ~CMCLEnumConnections()
|
|
{
|
|
if (m_rgpcd != NULL)
|
|
{
|
|
for (size_t i=0;i<m_cConnections;i++)
|
|
{
|
|
if (m_rgpcd[i].pUnk != NULL)
|
|
m_rgpcd[i].pUnk->Release();
|
|
}
|
|
delete [] m_rgpcd;
|
|
}
|
|
}
|
|
/*--------------------------------------
|
|
IUnknown implementation
|
|
--------------------------------------*/
|
|
public:
|
|
// IUnknown::AddRef implementation
|
|
ULONG __stdcall AddRef()
|
|
{
|
|
if (m_pThis != NULL)
|
|
m_pThis->AddRef();
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
// IUnknown::Release implementation
|
|
ULONG __stdcall Release()
|
|
{
|
|
if (m_pThis != NULL)
|
|
m_pThis->Release();
|
|
|
|
ULONG cRef = InterlockedDecrement(&m_cRef);
|
|
|
|
if(cRef != 0)
|
|
return cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
// IUnknown::QueryInterface implementation
|
|
HRESULT __stdcall QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
if(riid == IID_IEnumConnections)
|
|
*ppv = static_cast<IEnumConnections*>(this);
|
|
else if(riid == IID_IUnknown)
|
|
*ppv = static_cast<IUnknown*>(this);
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
/*--------------------------------------
|
|
IEnumConnections implementation
|
|
--------------------------------------*/
|
|
public:
|
|
// IEnumConnections::Next implementation
|
|
HRESULT __stdcall Next(ULONG cConnections, CONNECTDATA* rgpcd, ULONG* pcFetched)
|
|
{
|
|
LONG nIndex = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
if(rgpcd == NULL || pcFetched == NULL)
|
|
return E_INVALIDARG;
|
|
*pcFetched = 0;
|
|
if (cConnections == 0)
|
|
return S_OK;
|
|
for (ULONG i=0;i<cConnections;i++)
|
|
{
|
|
InterlockedExchange(&nIndex, m_nIndex);
|
|
if (nIndex >= (LONG)m_cConnections)
|
|
{
|
|
hr = S_FALSE;
|
|
break;
|
|
}
|
|
rgpcd[i] = m_rgpcd[nIndex];
|
|
if(rgpcd[i].pUnk != NULL)
|
|
rgpcd[i].pUnk->AddRef();
|
|
(*pcFetched)++;
|
|
InterlockedIncrement(&m_nIndex);
|
|
}
|
|
return hr;
|
|
}
|
|
// IEnumConnections::Skip implementation
|
|
HRESULT __stdcall Skip(ULONG cConnections)
|
|
{
|
|
LONG nIndex = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
InterlockedExchange(&nIndex, m_nIndex);
|
|
if (nIndex >= (LONG)m_cConnections)
|
|
return S_FALSE;
|
|
for (ULONG i=0;i<cConnections;i++)
|
|
{
|
|
nIndex = InterlockedIncrement(&m_nIndex);
|
|
if (nIndex >= (LONG)m_cConnections)
|
|
{
|
|
hr = S_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
// IEnumConnections::Reset implementation
|
|
HRESULT __stdcall Reset()
|
|
{
|
|
InterlockedExchange(&m_nIndex, 0);
|
|
return S_OK;
|
|
}
|
|
// IEnumConnections::Clone implementation
|
|
HRESULT __stdcall Clone(IEnumConnections** ppEnum)
|
|
{
|
|
CMCLEnumConnections* pNew = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
if(ppEnum == NULL)
|
|
return E_INVALIDARG;
|
|
*ppEnum = NULL;
|
|
pNew = new CMCLEnumConnections(m_pThis,(ULONG) m_cConnections, m_rgpcd);
|
|
if(pNew == NULL)
|
|
return E_OUTOFMEMORY;
|
|
pNew->m_nIndex = m_nIndex;
|
|
hr = pNew->QueryInterface(IID_IEnumConnections, (void**)ppEnum);
|
|
pNew->Release();
|
|
return hr;
|
|
}
|
|
/*--------------------------------------
|
|
CMCLEnumConnectionPoints Properties
|
|
--------------------------------------*/
|
|
private:
|
|
long m_cRef;
|
|
IUnknown* m_pThis; // Containing IUnknown for ref counting
|
|
long m_nIndex; // Index of current element
|
|
ULONG m_cConnections; // Number of connections in the array
|
|
CONNECTDATA* m_rgpcd; // Array of connections
|
|
};
|
|
|
|
// Globals
|
|
extern CMCLModule* g_pModule;
|
|
|
|
template<const IID* piid>
|
|
class CMCLConnectionPointImpl : public IConnectionPoint
|
|
{
|
|
/*--------------------------------------
|
|
Construction/destruction
|
|
--------------------------------------*/
|
|
public:
|
|
// CMCLConnectionPointImpl default constructor
|
|
CMCLConnectionPointImpl()
|
|
{
|
|
m_cRef = 1;
|
|
m_pCPC = NULL;
|
|
m_nNextCookie = 0;
|
|
}
|
|
// CMCLConnectionPointImpl constructor from container reference
|
|
CMCLConnectionPointImpl(IConnectionPointContainer* pCPC)
|
|
{
|
|
m_cRef = 1;
|
|
m_pCPC = NULL;
|
|
m_nNextCookie = 0;
|
|
m_pCPC = pCPC;
|
|
}
|
|
// CMCLConnectionPointImpl destructor
|
|
virtual ~CMCLConnectionPointImpl()
|
|
{
|
|
std::vector<CONNECTDATA*>::iterator it;
|
|
CONNECTDATA* pConnData = NULL;
|
|
|
|
for(it = m_vecpcd.begin(); it != m_vecpcd.end(); it++)
|
|
{
|
|
pConnData = *it;
|
|
if (pConnData != NULL)
|
|
{
|
|
if (pConnData->pUnk != NULL)
|
|
pConnData->pUnk->Release();
|
|
delete pConnData;
|
|
}
|
|
}
|
|
m_vecpcd.clear();
|
|
}
|
|
/*--------------------------------------
|
|
IUnknown implementation
|
|
--------------------------------------*/
|
|
public:
|
|
// IUnknown::AddRef implementation
|
|
ULONG __stdcall AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
// IUnknown::Release implementation
|
|
ULONG __stdcall Release()
|
|
{
|
|
ULONG cRef = InterlockedDecrement(&m_cRef);
|
|
if(cRef != 0)
|
|
return cRef;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
// IUnknown::QueryInterface implementation
|
|
HRESULT __stdcall QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
if(riid == IID_IConnectionPoint)
|
|
*ppv = static_cast<IConnectionPoint*>(this);
|
|
else if(riid == IID_IUnknown)
|
|
*ppv = static_cast<IUnknown*>(this);
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
/*--------------------------------------
|
|
IConnectionPoint implementation
|
|
--------------------------------------*/
|
|
// IConnectionPoint::GetConnectionInterface implementation
|
|
HRESULT __stdcall GetConnectionInterface(IID *pIID)
|
|
{
|
|
if (pIID == NULL)
|
|
return E_INVALIDARG;
|
|
*pIID = *piid;
|
|
return S_OK;
|
|
}
|
|
// IConnectionPoint::GetConnectionPointContainer implementation
|
|
HRESULT __stdcall GetConnectionPointContainer(IConnectionPointContainer** ppCPC)
|
|
{
|
|
if (ppCPC == NULL)
|
|
return E_POINTER;
|
|
if (m_pCPC == NULL)
|
|
return E_UNEXPECTED;
|
|
*ppCPC = m_pCPC;
|
|
if (*ppCPC != NULL)
|
|
(*ppCPC)->AddRef();
|
|
return S_OK;
|
|
}
|
|
// IConnectionPoint::Advise implementation
|
|
HRESULT __stdcall Advise(IUnknown* pUnk, DWORD* pdwCookie)
|
|
{
|
|
IUnknown* pSink = NULL;
|
|
long nCookie = 0;
|
|
CONNECTDATA* pConnData = NULL;
|
|
|
|
ModuleLock lock;
|
|
if (pUnk == NULL || pdwCookie == NULL)
|
|
return E_POINTER;
|
|
*pdwCookie = 0;
|
|
if(FAILED(pUnk->QueryInterface(*piid, (void**)&pSink)))
|
|
return CONNECT_E_CANNOTCONNECT;
|
|
pConnData = new CONNECTDATA;
|
|
if (pConnData == NULL)
|
|
return E_OUTOFMEMORY;
|
|
nCookie = InterlockedIncrement(&m_nNextCookie);
|
|
pConnData->dwCookie = (DWORD)nCookie;
|
|
pConnData->pUnk = pUnk;
|
|
m_vecpcd.push_back(pConnData);
|
|
return S_OK;
|
|
}
|
|
// IConnectionPoint::Unadvise implementation
|
|
HRESULT __stdcall Unadvise(DWORD dwCookie)
|
|
{
|
|
std::vector<CONNECTDATA*>::iterator it;
|
|
CONNECTDATA* pConnData = NULL;
|
|
bool bFound = false;
|
|
|
|
ModuleLock lock;
|
|
if(dwCookie == 0)
|
|
return CONNECT_E_NOCONNECTION;
|
|
for(it = m_vecpcd.begin(); it != m_vecpcd.end(); it++)
|
|
{
|
|
pConnData = *it;
|
|
if (pConnData != NULL)
|
|
{
|
|
if (pConnData->dwCookie == dwCookie)
|
|
{
|
|
bFound = true;
|
|
if (pConnData->pUnk != NULL)
|
|
pConnData->pUnk->Release();
|
|
delete pConnData;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (bFound)
|
|
{
|
|
m_vecpcd.erase(it);
|
|
return S_OK;
|
|
}
|
|
return CONNECT_E_NOCONNECTION;
|
|
}
|
|
// IConnectionPoint::EnumConnections implementation
|
|
HRESULT __stdcall EnumConnections(IEnumConnections** ppEnum)
|
|
{
|
|
CMCLEnumConnections* pEnum = NULL;
|
|
|
|
ModuleLock lock;
|
|
if (ppEnum == NULL)
|
|
return E_POINTER;
|
|
*ppEnum = NULL;
|
|
pEnum = new CMCLEnumConnections(this, m_vecpcd);
|
|
if (pEnum == NULL)
|
|
return E_OUTOFMEMORY;
|
|
if (FAILED(pEnum->QueryInterface(IID_IEnumConnections, (void**)ppEnum)))
|
|
return E_UNEXPECTED;
|
|
return S_OK;
|
|
}
|
|
/*--------------------------------------
|
|
CConnectionPoint Properties
|
|
--------------------------------------*/
|
|
private:
|
|
long m_cRef; // Reference count
|
|
IConnectionPointContainer* m_pCPC; // Pointer to parent container
|
|
long m_nNextCookie; // Next available cookie value
|
|
std::vector<CONNECTDATA*> m_vecpcd; // Array of connections
|
|
};
|
|
|
|
/*------------------------------------------------------------------------------
|
|
Begin: Standard Class factory implementation.
|
|
------------------------------------------------------------------------------*/
|
|
template<class T>
|
|
class CMCLFactoryImpl : public IClassFactory
|
|
{
|
|
public:
|
|
// Construction/destruction
|
|
CMCLFactoryImpl() : m_cRef(1) { }
|
|
virtual ~CMCLFactoryImpl() { }
|
|
|
|
// IClassFactory::AddRef implementation
|
|
ULONG __stdcall AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
// IClassFactory::Release implementation
|
|
ULONG __stdcall Release()
|
|
{
|
|
ULONG cRef = InterlockedDecrement(&m_cRef);
|
|
if(cRef != 0)
|
|
return cRef;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
// IClassFactory::QueryInterface implementation
|
|
HRESULT __stdcall QueryInterface(REFIID iid, void** ppv)
|
|
{
|
|
if((iid == IID_IUnknown) || (iid == IID_IClassFactory))
|
|
*ppv = (IClassFactory*)this;
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
// IClassFactory::CreateInstance implementation
|
|
virtual HRESULT __stdcall CreateInstance(IUnknown *pUnknownOuter, REFIID iid, void** ppv)
|
|
{
|
|
if(pUnknownOuter != NULL)
|
|
return CLASS_E_NOAGGREGATION;
|
|
|
|
#if defined (mclcommain_h) || defined (mclxlmain_published_api_h)
|
|
if (!mclComCheckMWComUtil())
|
|
{
|
|
const char* mcrversion=NULL;
|
|
mclGetMCRVersion(&mcrversion);
|
|
std::string errmsg;
|
|
errmsg = "MWComUtil ";
|
|
errmsg.append(mcrversion);
|
|
errmsg.append(" could not be found in the registry. Please refer to MATLAB Compiler SDK documentation for instructions on how to install and register MWComUtil.");
|
|
T::Error(errmsg.c_str());
|
|
return E_FAIL;
|
|
}
|
|
#endif
|
|
|
|
T* p = new T;
|
|
|
|
if(p == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Call the Init method to load the type information
|
|
if (!p->Init())
|
|
{
|
|
delete p;
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT hr = p->QueryInterface(iid, ppv);
|
|
p->Release();
|
|
return hr;
|
|
}
|
|
// IClassFactory::LockServer implementation
|
|
HRESULT __stdcall LockServer(BOOL bLock)
|
|
{
|
|
if(bLock)
|
|
g_pModule->Lock();
|
|
else
|
|
g_pModule->Unlock();
|
|
return S_OK;
|
|
}
|
|
private:
|
|
long m_cRef; // Ref count
|
|
};
|
|
|
|
/*------------------------------------------------------------------------------
|
|
End: Standard Class factory implementation.
|
|
------------------------------------------------------------------------------*/
|
|
|
|
// Add piidEvents so that Function Wizard works correctly (g1465604)
|
|
template<typename T, const IID* piid, typename T1, const CLSID* pclsid, const IID* piidEvents = nullptr>
|
|
class CMCLBaseImpl: public T, public ISupportErrorInfo, public IConnectionPointContainer
|
|
{
|
|
/*--------------------------------------
|
|
Construction/destruction
|
|
--------------------------------------*/
|
|
public:
|
|
// CMCLBaseImpl constructor
|
|
CMCLBaseImpl()
|
|
{
|
|
m_cRef = 1;
|
|
m_pTypeInfo = NULL;
|
|
m_pEvents = NULL;
|
|
g_pModule->Lock();
|
|
}
|
|
// CMCLBaseImpl destructor
|
|
virtual ~CMCLBaseImpl()
|
|
{
|
|
if (m_pEvents != NULL)
|
|
m_pEvents->Release();
|
|
if (m_pTypeInfo != NULL)
|
|
m_pTypeInfo->Release();
|
|
g_pModule->Unlock();
|
|
}
|
|
|
|
/*--------------------------------------
|
|
IUnknown implementation
|
|
--------------------------------------*/
|
|
public:
|
|
// IUnknown::AddRef implementation
|
|
ULONG __stdcall AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
// IUnknown::Release implementation
|
|
ULONG __stdcall Release()
|
|
{
|
|
ULONG cRef = InterlockedDecrement(&m_cRef);
|
|
if(cRef != 0)
|
|
return cRef;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
// IUnknown::QueryInterface implementation
|
|
HRESULT __stdcall QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
if(riid == *piid)
|
|
*ppv = static_cast<T*>(this);
|
|
else if(riid == IID_IUnknown)
|
|
*ppv = reinterpret_cast<IUnknown*>(this);
|
|
else if(riid == IID_IDispatch)
|
|
*ppv = reinterpret_cast<IDispatch*>(this);
|
|
else if(riid == IID_ISupportErrorInfo)
|
|
*ppv = static_cast<ISupportErrorInfo*>(this);
|
|
else if (riid == IID_IConnectionPointContainer)
|
|
*ppv = static_cast<IConnectionPointContainer*>(this);
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
/*--------------------------------------
|
|
IDispatch implementation
|
|
--------------------------------------*/
|
|
// IDispatch::GetTypeInfoCount implementation
|
|
HRESULT __stdcall GetTypeInfoCount(UINT* pCountTypeInfo)
|
|
{
|
|
if (pCountTypeInfo != NULL)
|
|
*pCountTypeInfo = 1;
|
|
return S_OK;
|
|
}
|
|
// IDispatch::GetTypeInfo implementation
|
|
HRESULT __stdcall GetTypeInfo(UINT iTypeInfo, LCID lcid, ITypeInfo** ppITypeInfo)
|
|
{
|
|
if (ppITypeInfo != NULL)
|
|
{
|
|
*ppITypeInfo = NULL;
|
|
if(iTypeInfo != 0)
|
|
return DISP_E_BADINDEX;
|
|
if (m_pTypeInfo != NULL)
|
|
m_pTypeInfo->AddRef();
|
|
*ppITypeInfo = m_pTypeInfo;
|
|
}
|
|
return S_OK;
|
|
}
|
|
// IDispatch::GetIDsOfNames implementation
|
|
HRESULT __stdcall GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
|
|
LCID lcid, DISPID* rgDispId)
|
|
{
|
|
if(riid != IID_NULL)
|
|
return DISP_E_UNKNOWNINTERFACE;
|
|
return DispGetIDsOfNames(m_pTypeInfo, rgszNames, cNames, rgDispId);
|
|
}
|
|
// IDispatch::Invoke implementation
|
|
HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
|
|
DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
|
|
UINT* puArgErr)
|
|
{
|
|
if(riid != IID_NULL)
|
|
return DISP_E_UNKNOWNINTERFACE;
|
|
return DispInvoke(this, m_pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
|
|
}
|
|
/*--------------------------------------
|
|
ISupportErrorInfo implementation
|
|
--------------------------------------*/
|
|
// ISupportErrorInfo::InterfaceSupportsErrorInfo implementation
|
|
HRESULT __stdcall InterfaceSupportsErrorInfo(REFIID riid)
|
|
{
|
|
if(riid == *piid)
|
|
return S_OK;
|
|
else
|
|
return S_FALSE;
|
|
}
|
|
/*--------------------------------------
|
|
IConnectionPointContainer implementation
|
|
--------------------------------------*/
|
|
// IConnectionPointContainer::EnumConnectionPoints implementation
|
|
HRESULT __stdcall EnumConnectionPoints(IEnumConnectionPoints** ppEnum)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG cConnections = 0;
|
|
CMCLEnumConnectionPoints* pEnum = NULL;
|
|
|
|
if (ppEnum == NULL)
|
|
return E_POINTER;
|
|
if (m_pEvents != NULL)
|
|
cConnections = 1;
|
|
pEnum = new CMCLEnumConnectionPoints(reinterpret_cast<IUnknown*>(this), cConnections, &m_pEvents);
|
|
if (pEnum == NULL)
|
|
return E_OUTOFMEMORY;
|
|
hr = pEnum->QueryInterface(IID_IEnumConnectionPoints, (void**)ppEnum);
|
|
pEnum->Release();
|
|
return hr;
|
|
}
|
|
// IConnectionPointContainer::FindConnectionPoint implementation
|
|
HRESULT __stdcall FindConnectionPoint(REFIID riid, IConnectionPoint** ppCP)
|
|
{
|
|
if (ppCP == NULL)
|
|
return E_POINTER;
|
|
if (piidEvents == NULL)
|
|
return CONNECT_E_NOCONNECTION;
|
|
if (riid == *piidEvents)
|
|
{
|
|
if (m_pEvents == NULL)
|
|
return CONNECT_E_NOCONNECTION;
|
|
*ppCP = m_pEvents;
|
|
(*ppCP)->AddRef();
|
|
return S_OK;
|
|
}
|
|
return CONNECT_E_NOCONNECTION;
|
|
}
|
|
/*--------------------------------------
|
|
CMCLBaseImpl Methods
|
|
--------------------------------------*/
|
|
// Initializes class, loads type info stuff and initializes connection point if necessary.
|
|
// Put any additional init stuff in here.
|
|
virtual bool Init(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = g_pModule->GetTypeInfo(*piid, &m_pTypeInfo);
|
|
|
|
if(FAILED(hr))
|
|
return false;
|
|
|
|
if (piidEvents != NULL)
|
|
{
|
|
CMCLConnectionPointImpl<piidEvents>* pEvents
|
|
= new CMCLConnectionPointImpl<piidEvents>(static_cast<IConnectionPointContainer*>(this));
|
|
if (pEvents == NULL)
|
|
return false;
|
|
|
|
if (pEvents != NULL)
|
|
{
|
|
if (FAILED(pEvents->QueryInterface(IID_IConnectionPoint, (void**)&m_pEvents)))
|
|
return false;
|
|
pEvents->Release();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
// Registers the class
|
|
static HRESULT __stdcall RegisterClass(const GUID* plibid, unsigned short wMajor, unsigned short wMinor, const char* lpszFriendlyName,
|
|
const char* lpszVerIndProgID, const char* lpszProgID, const char* lpszModuleName)
|
|
{
|
|
return mclRegisterServer(lpszModuleName, *pclsid, *plibid, wMajor, wMinor, lpszFriendlyName,
|
|
lpszVerIndProgID, lpszProgID, "Both");
|
|
}
|
|
// Unregisters the class
|
|
static HRESULT __stdcall UnregisterClass(const char* lpszVerIndProgID, const char* lpszProgID)
|
|
{
|
|
return mclUnregisterServer(*pclsid, lpszVerIndProgID, lpszProgID);
|
|
}
|
|
// Called by COM framework to get IClassFactory pointer on which to create new instances of object
|
|
static HRESULT __stdcall GetClassObject(REFCLSID clsid, REFIID iid, void** ppv)
|
|
{
|
|
if(clsid != *pclsid)
|
|
return CLASS_E_CLASSNOTAVAILABLE;
|
|
|
|
CMCLFactoryImpl<T1>* pFactory = new CMCLFactoryImpl<T1>;
|
|
if(pFactory == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// QueryInterface for IClassFactory
|
|
HRESULT hr = pFactory->QueryInterface(iid, ppv);
|
|
pFactory->Release();
|
|
return hr;
|
|
}
|
|
// Method to report an error
|
|
static HRESULT __stdcall Error(const char* lpszMessage)
|
|
{
|
|
ICreateErrorInfo* pCreateErrorInfo = NULL;
|
|
IErrorInfo* pErrorInfo = NULL;
|
|
HRESULT hr = S_OK;
|
|
OLECHAR* lpwszMessage = NULL;
|
|
OLECHAR* lpwszSource = NULL;
|
|
const char* lpszSource = NULL;
|
|
int nLen = 0;
|
|
|
|
if (FAILED(hr = CreateErrorInfo(&pCreateErrorInfo)))
|
|
goto EXIT;
|
|
// Set message text
|
|
if (lpszMessage != NULL)
|
|
{
|
|
pwcsStackPointer slpwszMessage = NULL;
|
|
initializeWcsStackPointer(&slpwszMessage);
|
|
if(mwMbstowcs(slpwszMessage, lpszMessage) < 0) {
|
|
deleteWcsStackPointer(slpwszMessage);
|
|
pCreateErrorInfo->SetDescription(L"Error converting multibyte string to wide character string.");
|
|
} else {
|
|
lpwszMessage = _wcsdup(reinterpret_cast<wchar_t*>(slpwszMessage->hPtr));
|
|
deleteWcsStackPointer(slpwszMessage);
|
|
pCreateErrorInfo->SetDescription(lpwszMessage);
|
|
}
|
|
|
|
}
|
|
else
|
|
pCreateErrorInfo->SetDescription(L"");
|
|
// Set IID
|
|
pCreateErrorInfo->SetGUID(*piid);
|
|
// Set error source
|
|
lpszSource = g_pModule->GetProgID(*pclsid);
|
|
if (lpszSource != NULL)
|
|
{
|
|
pwcsStackPointer slpwszSource = NULL;
|
|
initializeWcsStackPointer(&slpwszSource);
|
|
if ( mwMbstowcs(slpwszSource, lpszSource) < 0){
|
|
deleteWcsStackPointer( slpwszSource );
|
|
pCreateErrorInfo->SetDescription(L"Error converting multibyte string to wide character string");
|
|
} else {
|
|
lpwszSource=_wcsdup(reinterpret_cast<wchar_t*>(slpwszSource->hPtr));
|
|
deleteWcsStackPointer( slpwszSource );
|
|
pCreateErrorInfo->SetSource(lpwszSource);
|
|
}
|
|
|
|
}
|
|
else
|
|
pCreateErrorInfo->SetSource(L"");
|
|
// Set error info
|
|
if (FAILED(hr = pCreateErrorInfo->QueryInterface(IID_IErrorInfo, (void**)&pErrorInfo)))
|
|
goto EXIT;
|
|
hr = SetErrorInfo(0, pErrorInfo);
|
|
EXIT:
|
|
if (lpwszMessage != NULL)
|
|
delete lpwszMessage;
|
|
if (lpwszSource != NULL)
|
|
delete lpwszSource;
|
|
if (pErrorInfo != NULL)
|
|
pErrorInfo->Release();
|
|
if (pCreateErrorInfo != NULL)
|
|
pCreateErrorInfo->Release();
|
|
return hr;
|
|
}
|
|
// Method to report an error from an EXCEPINFO structure (note: does not use template *piid in SetGUID)
|
|
static HRESULT __stdcall Error(REFIID riid, EXCEPINFO* pExcepInfo)
|
|
{
|
|
ICreateErrorInfo* pCreateErrorInfo = NULL;
|
|
IErrorInfo* pErrorInfo = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pExcepInfo == NULL)
|
|
goto EXIT;
|
|
if (FAILED(hr = CreateErrorInfo(&pCreateErrorInfo)))
|
|
goto EXIT;
|
|
// Set message text
|
|
pCreateErrorInfo->SetDescription(pExcepInfo->bstrDescription);
|
|
// Set IID
|
|
pCreateErrorInfo->SetGUID(riid);
|
|
// Set error source
|
|
pCreateErrorInfo->SetSource(pExcepInfo->bstrSource);
|
|
// Set error info
|
|
if (FAILED(hr = pCreateErrorInfo->QueryInterface(IID_IErrorInfo, (void**)&pErrorInfo)))
|
|
goto EXIT;
|
|
hr = SetErrorInfo(0, pErrorInfo);
|
|
EXIT:
|
|
if (pErrorInfo != NULL)
|
|
pErrorInfo->Release();
|
|
if (pCreateErrorInfo != NULL)
|
|
pCreateErrorInfo->Release();
|
|
return hr;
|
|
}
|
|
protected:
|
|
int RequestLocalLock()
|
|
{
|
|
mclAcquireMutex();
|
|
return 0;
|
|
}
|
|
int ReleaseLocalLock()
|
|
{
|
|
mclReleaseMutex();
|
|
return 0;
|
|
}
|
|
/*--------------------------------------
|
|
CMCLBaseImpl Properties
|
|
--------------------------------------*/
|
|
private:
|
|
long m_cRef; // Reference count
|
|
ITypeInfo* m_pTypeInfo; // Type Info pointer
|
|
protected:
|
|
IConnectionPoint* m_pEvents;// Connection point for objects that implement an event interface
|
|
};
|
|
|
|
template<class T>
|
|
class CMCLSingleFactoryImpl : public CMCLFactoryImpl<T>
|
|
{
|
|
public:
|
|
// IClassFactory::CreateInstance implementation
|
|
HRESULT __stdcall CreateInstance(IUnknown *pUnknownOuter, REFIID iid, void** ppv)
|
|
{
|
|
ModuleLock lock;
|
|
if(pUnknownOuter != NULL)
|
|
return CLASS_E_NOAGGREGATION;
|
|
if (m_p == NULL)
|
|
{
|
|
m_p = new T;
|
|
if(m_p == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
if (!m_p->Init())
|
|
{
|
|
delete m_p;
|
|
m_p = NULL;
|
|
return E_UNEXPECTED;
|
|
}
|
|
m_p->AddRef();
|
|
}
|
|
HRESULT hr = m_p->QueryInterface(iid, ppv);
|
|
m_p->Release();
|
|
return hr;
|
|
}
|
|
private:
|
|
long m_cRef; // Ref count
|
|
static T* m_p; // Static instance of T
|
|
};
|
|
|
|
template<class T, const IID* piid, class T1, const CLSID* pclsid>
|
|
class CMCLSingleBaseImpl: public CMCLBaseImpl<T, piid, T1, pclsid>
|
|
{
|
|
/*--------------------------------------
|
|
Construction/destruction
|
|
--------------------------------------*/
|
|
public:
|
|
// CMCLSingleBaseImpl constructor
|
|
CMCLSingleBaseImpl(){}
|
|
// CMCLSingleBaseImpl destructor
|
|
virtual ~CMCLSingleBaseImpl(){}
|
|
/*--------------------------------------
|
|
IUnknown implementation
|
|
--------------------------------------*/
|
|
public:
|
|
// IUnknown::AddRef implementation
|
|
ULONG __stdcall AddRef()
|
|
{
|
|
return 2;
|
|
}
|
|
// IUnknown::Release implementation
|
|
ULONG __stdcall Release()
|
|
{
|
|
return 1;
|
|
}
|
|
// Called by COM framework to get IClassFactory pointer on which to create new instances of object
|
|
static HRESULT __stdcall GetClassObject(REFCLSID clsid, REFIID iid, void** ppv)
|
|
{
|
|
if(clsid != *pclsid)
|
|
return CLASS_E_CLASSNOTAVAILABLE;
|
|
|
|
CMCLSingleFactoryImpl<T1>* pFactory = new CMCLSingleFactoryImpl<T1>;
|
|
if(pFactory == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// QueryInterface for IClassFactory
|
|
HRESULT hr = pFactory->QueryInterface(iid, ppv);
|
|
pFactory->Release();
|
|
return hr;
|
|
}
|
|
};
|
|
|
|
template<class T, const IID* piid, class T1, const CLSID* pclsid>
|
|
class CMCLClassImpl: public CMCLBaseImpl<T, piid, T1, pclsid>, public IMCLEvent
|
|
{
|
|
/*--------------------------------------
|
|
Construction/destruction
|
|
--------------------------------------*/
|
|
public:
|
|
// CMCLClassImpl constructor
|
|
CMCLClassImpl()
|
|
{
|
|
m_pFlags = NULL;
|
|
m_hinst = NULL;
|
|
}
|
|
// CMCLClassImpl destructor
|
|
virtual ~CMCLClassImpl()
|
|
{
|
|
if (m_pFlags != NULL)
|
|
m_pFlags->Release();
|
|
}
|
|
protected:
|
|
void RegisterListener()
|
|
{
|
|
g_pModule->getEventMap()->add(m_hinst, static_cast<IMCLEvent*>(this));
|
|
}
|
|
void UnregisterListener()
|
|
{
|
|
g_pModule->getEventMap()->remove(m_hinst, static_cast<IMCLEvent*>(this));
|
|
}
|
|
public:
|
|
/*--------------------------------------
|
|
CMCLClassImpl Methods
|
|
--------------------------------------*/
|
|
// Registers the class and adds it to the MatLab XL component catagory
|
|
static HRESULT __stdcall RegisterClass(const GUID* plibid, unsigned short wMajor, unsigned short wMinor, const char* lpszFriendlyName,
|
|
const char* lpszVerIndProgID, const char* lpszProgID, const char* lpszModuleName)
|
|
{
|
|
return mclRegisterMatLabComponent(lpszModuleName, pclsid, plibid, wMajor, wMinor, lpszFriendlyName, lpszVerIndProgID, lpszProgID);
|
|
}
|
|
// Unregisters the class and removes it from the MatLab XL component catagory
|
|
static HRESULT __stdcall UnregisterClass(const char* lpszVerIndProgID, const char* lpszProgID)
|
|
{
|
|
return mclUnRegisterMatLabComponent(pclsid, lpszVerIndProgID, lpszProgID);
|
|
}
|
|
// Returns a pointer to the contained MWFlags object
|
|
HRESULT __stdcall get_MWFlags(IMWFlags** ppFlags)
|
|
{
|
|
HRESULT hr = S_OK; // Return code
|
|
ModuleLock lock;
|
|
if (ppFlags == NULL)
|
|
return E_INVALIDARG;
|
|
*ppFlags = NULL;
|
|
// If there is not one already allocated, creat a new one.
|
|
if (m_pFlags == NULL)
|
|
{
|
|
hr = CoCreateInstance(CLSID_MWFlags, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IMWFlags, (void**)&m_pFlags);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
*ppFlags = m_pFlags;
|
|
m_pFlags->AddRef();
|
|
return hr;
|
|
}
|
|
// Sets the array-formatting-object
|
|
HRESULT __stdcall put_MWFlags(IMWFlags* pFlags)
|
|
{
|
|
HRESULT hr = S_OK; // Return code
|
|
ModuleLock lock;
|
|
if (pFlags == NULL)
|
|
return E_INVALIDARG;
|
|
// Set internal object to new one.
|
|
if (m_pFlags != NULL)
|
|
m_pFlags->Release();
|
|
m_pFlags = pFlags;
|
|
m_pFlags->AddRef();
|
|
return hr;
|
|
}
|
|
// Call the MatLab mex funtion pointed to by mlxF
|
|
HRESULT CallComFcn(const char* name, int nargout, int fnout, int fnin, ...)
|
|
{
|
|
mxArray **plhs;
|
|
//int nargout: comes in as ip param to this function
|
|
int alloc_nargout = (nargout == 0) ? 1: nargout;
|
|
mxArray **prhs;
|
|
int nargin = 0;
|
|
|
|
VARIANT **plvar;
|
|
const VARIANT **prvar;
|
|
|
|
bool bVarargout = fnout < 0;
|
|
bool bVarargin = fnin < 0;
|
|
|
|
mxArray *varargout = NULL;
|
|
mxArray *varargin = NULL;
|
|
|
|
int i;
|
|
int new_nargin;
|
|
va_list ap;
|
|
bool bFoundDefault = false;
|
|
HRESULT retval = S_OK;
|
|
_MCLCONVERSION_FLAGS flags;
|
|
IMWFlags* pFlags = NULL;
|
|
bool bRet = false;
|
|
int dc_status = 0;
|
|
|
|
// Check MCR instance
|
|
if (!m_hinst)
|
|
{
|
|
this->Error("MCR instance is not available");
|
|
return E_FAIL;
|
|
}
|
|
// Get Conversion flags
|
|
if (FAILED(get_MWFlags(&pFlags)))
|
|
{
|
|
this->Error("Error getting data conversion flags");
|
|
return E_FAIL;
|
|
}
|
|
if (FAILED(GetConversionFlags(pFlags, &flags)))
|
|
{
|
|
this->Error("Error getting data conversion flags");
|
|
return E_FAIL;
|
|
}
|
|
pFlags->Release();
|
|
try
|
|
{
|
|
// O/P from mclMlxFeval (lhs)
|
|
va_start( ap, fnin );
|
|
if (bVarargout)
|
|
{
|
|
fnout = -fnout;
|
|
}
|
|
plvar = (VARIANT **) _alloca( fnout * sizeof( VARIANT * ) );
|
|
for (i=0; i< fnout; i++)
|
|
{
|
|
plvar[i] = va_arg( ap, VARIANT *);
|
|
}
|
|
plhs = (mxArray **) _alloca( alloc_nargout * sizeof( mxArray * ));
|
|
for (i=0; i< alloc_nargout; i++)
|
|
{
|
|
plhs[i] = NULL;
|
|
}
|
|
// I/P to mclMlxFeval (rhs)
|
|
if (bVarargin)
|
|
{
|
|
fnin = -fnin;
|
|
}
|
|
prvar = (const VARIANT **) _alloca( fnin * sizeof(VARIANT *));
|
|
for (i = 0; i< fnin; i++)
|
|
{
|
|
prvar[i] = va_arg( ap, VARIANT *);
|
|
if (IsVisualBasicDefault( prvar[i] ))
|
|
{
|
|
bFoundDefault = true;
|
|
}
|
|
else
|
|
{
|
|
if (bFoundDefault)
|
|
{
|
|
this->Error( "Error: Arguments may only be defaulted at the end of an argument list" );
|
|
retval = E_FAIL;
|
|
goto finish;
|
|
}
|
|
nargin++;
|
|
}
|
|
}
|
|
if (bVarargin && !bFoundDefault)
|
|
{
|
|
//g830130 - if there is only one varargin, and MCLUtil.MWPack() is not called,
|
|
//the varargin won't be passed in as an array. In this case, the conversion flag's
|
|
//nInputInd shoulnd't be incremented.
|
|
if (prvar[fnin-1]->vt == (VT_VARIANT|VT_BYREF))
|
|
{
|
|
if ((prvar[fnin-1]->pvarVal != NULL) && (prvar[fnin-1]->pvarVal->vt & VT_ARRAY))
|
|
{
|
|
flags.nInputInd += 1;
|
|
}
|
|
}
|
|
if ((dc_status = Variant2mxArray( prvar[fnin-1], &varargin, &flags)) < 0)
|
|
{
|
|
mclSetLastErrIdAndMsg("", GetCOMErrorMessage(dc_status));
|
|
retval = E_FAIL;
|
|
goto finish;
|
|
}
|
|
flags.nInputInd -= 1;
|
|
if (varargin == NULL)
|
|
nargin -= 1;
|
|
/*Empty varargin should be passed to MATLAB as a empty cell array*/
|
|
else if(mxIsEmpty(varargin))
|
|
{
|
|
varargin = NULL;
|
|
nargin -= 1;
|
|
}
|
|
else if (mxGetClassID(varargin) == mxCELL_CLASS)
|
|
nargin += static_cast<int>(mxGetN( varargin )) - 1;
|
|
}
|
|
prhs = (mxArray **) _alloca( nargin * sizeof(mxArray *));
|
|
for (i = 0; i< nargin; i++)
|
|
prhs[i] = NULL;
|
|
mclmxarray_list protect_plhs( nargout, plhs );
|
|
mclmxarray_list protect_prhs( nargin, prhs );
|
|
for (i = 0; i< nargin; i++)
|
|
{
|
|
if (i < fnin-1 || (!bVarargin && i == fnin-1))
|
|
{
|
|
if ((dc_status = Variant2mxArray( prvar[i], &prhs[i], &flags)) < 0)
|
|
{
|
|
mclSetLastErrIdAndMsg("", GetCOMErrorMessage(dc_status));
|
|
retval = E_FAIL;
|
|
goto finish;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( varargin != NULL && mxGetClassID(varargin) == mxCELL_CLASS )
|
|
prhs[i] = mclCreateSharedCopy(((mxArray **)mxGetData(varargin))[i-fnin+1]);
|
|
else
|
|
prhs[i] = mclCreateSharedCopy(varargin);
|
|
}
|
|
}
|
|
bFoundDefault = false;
|
|
new_nargin = nargin;
|
|
for (i = 0; i< nargin; i++)
|
|
{
|
|
if (!bFoundDefault) {
|
|
if (prhs[i] == NULL) {
|
|
bFoundDefault = true;
|
|
new_nargin = i;
|
|
}
|
|
}
|
|
else {
|
|
if (prhs[i] != NULL) {
|
|
this->Error( "Error: Arguments may only be defaulted at the end of an argument list" );
|
|
retval = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
if (retval != E_FAIL) {
|
|
nargin = new_nargin;
|
|
// call mclMlxFeval
|
|
bRet = g_pModule->Feval(m_hinst, name, nargout, plhs, nargin, prhs);
|
|
if (!bRet)
|
|
goto finish;
|
|
// translate o/p (lhs) of mclMlxFeval back to variant
|
|
if (nargout == 0 && plhs[0] != NULL )
|
|
{
|
|
mxDestroyArray( plhs[0] );
|
|
plhs[0] = NULL;
|
|
}
|
|
if (bVarargout && nargout >= fnout)
|
|
{
|
|
varargout = mclCreateCellArrayFromArrayList(nargout-fnout+1, &plhs[fnout-1]);
|
|
}
|
|
for (i = 0; i<fnout && i<nargout; i++)
|
|
{
|
|
if (i < fnout -1 || (!bVarargout && i == fnout-1 ))
|
|
{
|
|
if ((dc_status = mxArray2Variant( plhs[i], plvar[i], &flags)) < 0)
|
|
{
|
|
mclSetLastErrIdAndMsg("", GetCOMErrorMessage(dc_status));
|
|
retval = E_FAIL;
|
|
goto finish;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flags.nOutputInd += 1;
|
|
flags.nTransposeInd += 1;
|
|
if ((dc_status = mxArray2Variant( varargout, plvar[i], &flags)) < 0)
|
|
{
|
|
mclSetLastErrIdAndMsg("", GetCOMErrorMessage(dc_status));
|
|
retval = E_FAIL;
|
|
goto finish;
|
|
}
|
|
flags.nOutputInd -= 1;
|
|
flags.nTransposeInd -= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
this->Error("Unexpected Error Thrown");
|
|
retval = E_FAIL;
|
|
}
|
|
finish:
|
|
if (varargin != NULL)
|
|
mxDestroyArray( varargin );
|
|
if (varargout != NULL)
|
|
mxDestroyArray( varargout );
|
|
if (!bRet)
|
|
{
|
|
const char* msg = g_pModule->getErrorMessage();
|
|
|
|
//If there is no error message on the CMCLModule (which might
|
|
// have run its work on a different thread), then check the
|
|
// local thread for an error message.
|
|
if(!msg)
|
|
{
|
|
msg = mclGetLastErrorMessage();
|
|
}
|
|
//If the g_pModule has an error (the thread that actually called feval),
|
|
// use that, otherwise see if this thread has an error,
|
|
// if all else fails, give a generic message.
|
|
this->Error((msg ? msg : "Unspecified error"));
|
|
retval = E_FAIL;
|
|
}
|
|
return(retval);
|
|
}
|
|
void mclRaiseEventA(const char* lpszName, DISPID dispid, int nargin, mxArray** prhs)
|
|
{
|
|
HRESULT hr = S_OK; // Return code
|
|
IEnumConnections* pEnum = NULL; // Pointer to enum of all connections
|
|
CONNECTDATA ConnData = {0,0}; // CONNECTDATA structure for each connection
|
|
ULONG cFetched = 0; // Number of connections fetched in a call
|
|
IDispatch* pDisp = NULL; // IDispatch pointer to make call on
|
|
VARIANT varResult; // Result from call
|
|
VARIANT* pvars = NULL; // Varaibles to send into call
|
|
DISPPARAMS disp = {NULL,NULL,0,0};// Disparams used to make call
|
|
// DISPID dispid = 0; // DISPID of method to invoke
|
|
OLECHAR* lpwszName = NULL; // Temp buffer for method name
|
|
int nLen = 0; // Length of name string
|
|
int i = 0; // Loop counter
|
|
_MCLCONVERSION_FLAGS flags; // Temp flags struct
|
|
IMWFlags* pFlags = NULL; // Temp flags object
|
|
|
|
pwcsStackPointer slpwszName = NULL;
|
|
initializeWcsStackPointer(&slpwszName);
|
|
|
|
// If no connection point available or invalid name, return
|
|
if (this->m_pEvents == NULL || lpszName == NULL)
|
|
return;
|
|
if (nargin < 0 || (prhs == NULL && nargin > 0))
|
|
nargin = 0;
|
|
// Get Conversion flags
|
|
if (FAILED(get_MWFlags(&pFlags)))
|
|
InitConversionFlags(&flags);
|
|
else
|
|
{
|
|
if (FAILED(GetConversionFlags(pFlags, &flags)))
|
|
InitConversionFlags(&flags);
|
|
pFlags->Release();
|
|
}
|
|
// Enum all connections
|
|
hr = this->m_pEvents->EnumConnections(&pEnum);
|
|
if (FAILED(hr))
|
|
goto EXIT;
|
|
VariantInit(&varResult);
|
|
// Create Array of Variants for call
|
|
if (nargin > 0)
|
|
{
|
|
pvars = new VARIANT[nargin];
|
|
if (pvars == NULL)
|
|
goto EXIT;
|
|
for (i=0;i<nargin;i++)
|
|
VariantInit(&pvars[i]);
|
|
disp.rgvarg = pvars;
|
|
disp.cArgs = nargin;
|
|
}
|
|
for (i=0;i<nargin;i++)
|
|
{
|
|
if (prhs[i] != NULL)
|
|
{
|
|
if (mxArray2Variant(prhs[i], &pvars[nargin-i-1], &flags) < 0)
|
|
goto EXIT;
|
|
}
|
|
}
|
|
// Get name
|
|
if (mwMbstowcs(slpwszName,lpszName) < 0) {
|
|
deleteWcsStackPointer(slpwszName);
|
|
goto EXIT;
|
|
}
|
|
lpwszName=_wcsdup(reinterpret_cast<wchar_t*>(slpwszName->hPtr));
|
|
deleteWcsStackPointer(slpwszName);
|
|
|
|
// Loop over all connections and call each one
|
|
pEnum->Reset();
|
|
for(;;)
|
|
{
|
|
hr = pEnum->Next(1, &ConnData, &cFetched);
|
|
if (cFetched == 0)
|
|
break;
|
|
if (ConnData.pUnk != NULL)
|
|
{
|
|
hr = ConnData.pUnk->QueryInterface(IID_IDispatch, (void**)&pDisp);
|
|
ConnData.pUnk->Release();
|
|
if (SUCCEEDED(hr) && pDisp != NULL)
|
|
{
|
|
g_pModule->RaiseEvent(lpwszName, dispid, pDisp, &disp);
|
|
pDisp->Release();
|
|
pDisp = NULL;
|
|
}
|
|
}
|
|
}
|
|
EXIT:
|
|
if (pEnum != NULL)
|
|
pEnum->Release();
|
|
if (pDisp != NULL)
|
|
pDisp->Release();
|
|
if (lpwszName != NULL)
|
|
delete lpwszName;
|
|
if (pvars != NULL)
|
|
{
|
|
for (i=0;i<nargin;i++)
|
|
VariantClear(&pvars[i]);
|
|
delete [] pvars;
|
|
}
|
|
}
|
|
// Gets a property from the array
|
|
HRESULT GetProperty(const char* name, VARIANT* pvarValue)
|
|
{
|
|
_MCLCONVERSION_FLAGS flags;
|
|
HRESULT hr = S_OK;
|
|
IMWFlags* pFlags = NULL;
|
|
mxArray* px = NULL;
|
|
int dc_status = 0;
|
|
const char* msg = NULL;
|
|
|
|
mwLock lock;
|
|
// Check MCR instance
|
|
if (!m_hinst)
|
|
{
|
|
this->Error("MCR instance is not available");
|
|
return E_FAIL;
|
|
}
|
|
if (pvarValue == NULL)
|
|
return E_INVALIDARG;
|
|
// Get Conversion flags
|
|
if (FAILED(get_MWFlags(&pFlags)))
|
|
{
|
|
this->Error("Error getting data conversion flags");
|
|
return E_FAIL;
|
|
}
|
|
if (FAILED(GetConversionFlags(pFlags, &flags)))
|
|
{
|
|
this->Error("Error getting data conversion flags");
|
|
return E_FAIL;
|
|
}
|
|
pFlags->Release();
|
|
try
|
|
{
|
|
if (!mclGetGlobal(m_hinst, name, &px))
|
|
{
|
|
const char* msg = mclGetLastErrorMessage();
|
|
this->Error((msg ? msg : "Unspecified error"));
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
if (pvarValue->vt != VT_EMPTY)
|
|
VariantClear(pvarValue);
|
|
if ((dc_status = mxArray2Variant(px, pvarValue, &flags)) < 0)
|
|
{
|
|
msg = GetCOMErrorMessage(dc_status);
|
|
this->Error((msg ? msg : "Unspecified error"));
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
this->Error("Unexpected Error Thrown");
|
|
hr = E_FAIL;
|
|
}
|
|
if (px)
|
|
mxDestroyArray(px);
|
|
return hr;
|
|
}
|
|
// Sets a property in the array
|
|
HRESULT PutProperty(const char* name, const VARIANT* pvarValue)
|
|
{
|
|
_MCLCONVERSION_FLAGS flags;
|
|
HRESULT hr = S_OK;
|
|
IMWFlags* pFlags = NULL;
|
|
mxArray* px = NULL;
|
|
int dc_status = 0;
|
|
const char* msg = NULL;
|
|
|
|
mwLock lock;
|
|
// Check MCR instance
|
|
if (!m_hinst)
|
|
{
|
|
this->Error("MCR instance is not available");
|
|
return E_FAIL;
|
|
}
|
|
if (pvarValue == NULL)
|
|
return E_INVALIDARG;
|
|
// Get Conversion flags
|
|
if (FAILED(get_MWFlags(&pFlags)))
|
|
{
|
|
this->Error("Error getting data conversion flags");
|
|
return E_FAIL;
|
|
}
|
|
if (FAILED(GetConversionFlags(pFlags, &flags)))
|
|
{
|
|
this->Error("Error getting data conversion flags");
|
|
return E_FAIL;
|
|
}
|
|
pFlags->Release();
|
|
try
|
|
{
|
|
if ((dc_status = Variant2mxArray(pvarValue, &px, &flags)) < 0)
|
|
{
|
|
msg = GetCOMErrorMessage(dc_status);
|
|
this->Error((msg ? msg : "Unspecified error"));
|
|
hr = E_FAIL;
|
|
}
|
|
else if (!mclSetGlobal(m_hinst, name, px))
|
|
{
|
|
msg = mclGetLastErrorMessage();
|
|
this->Error((msg ? msg : "Unspecified error"));
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
this->Error("Unexpected Error Thrown");
|
|
hr = E_FAIL;
|
|
}
|
|
if (px)
|
|
mxDestroyArray(px);
|
|
return hr;
|
|
}
|
|
/*--------------------------------------
|
|
CMCLClassImpl Properties
|
|
--------------------------------------*/
|
|
private:
|
|
IMWFlags* m_pFlags; // Pointer to a formatting/conversion-flags object
|
|
protected:
|
|
HMCRINSTANCE m_hinst; // MCR instance
|
|
};
|
|
|
|
template<class T, const IID* piid, class T1, const CLSID* pclsid>
|
|
class CMCLSingleImpl: public CMCLClassImpl<T, piid, T1, pclsid>
|
|
{
|
|
/*--------------------------------------
|
|
Construction/destruction
|
|
--------------------------------------*/
|
|
public:
|
|
// CMCLSingleImpl constructor
|
|
CMCLSingleImpl(){}
|
|
// CMCLSingleImpl destructor
|
|
virtual ~CMCLSingleImpl(){}
|
|
/*--------------------------------------
|
|
IUnknown implementation
|
|
--------------------------------------*/
|
|
public:
|
|
// IUnknown::AddRef implementation
|
|
ULONG __stdcall AddRef()
|
|
{
|
|
return 2;
|
|
}
|
|
// IUnknown::Release implementation
|
|
ULONG __stdcall Release()
|
|
{
|
|
return 1;
|
|
}
|
|
// Called by COM framework to get IClassFactory pointer on which to create new instances of object
|
|
static HRESULT __stdcall GetClassObject(REFCLSID clsid, REFIID iid, void** ppv)
|
|
{
|
|
if(clsid != *pclsid)
|
|
return CLASS_E_CLASSNOTAVAILABLE;
|
|
|
|
CMCLSingleFactoryImpl<T1>* pFactory = new CMCLSingleFactoryImpl<T1>;
|
|
if(pFactory == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// QueryInterface for IClassFactory
|
|
HRESULT hr = pFactory->QueryInterface(iid, ppv);
|
|
pFactory->Release();
|
|
return hr;
|
|
}
|
|
};
|
|
|
|
/*------------------------------------------------------------------------------
|
|
Singleton Class factory implementation.
|
|
------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
template<typename Iterface>
|
|
struct MarshalHelperDelegate
|
|
{
|
|
template<typename Derived>
|
|
static HRESULT copyMWFlags(MCLCONVERSION_FLAGS flags, void * receiver)
|
|
{
|
|
HRESULT hr;
|
|
Derived * derivedObject = static_cast<Derived*>(/*this*/receiver);
|
|
IMWFlags * mwflags = NULL;
|
|
if(FAILED(hr = derivedObject->get_MWFlags(&mwflags)))
|
|
goto EXIT;
|
|
HRESULT mclPutConversionFlags(IMWFlags* pFlags, MCLCONVERSION_FLAGS flags);
|
|
if(FAILED(hr = mclPutConversionFlags(mwflags, flags)))
|
|
goto EXIT;
|
|
if(FAILED(derivedObject->put_MWFlags(mwflags)))
|
|
goto EXIT;
|
|
EXIT:
|
|
if(mwflags)
|
|
mwflags->Release();
|
|
return hr;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct MarshalHelperDelegate<IMWStruct>
|
|
{
|
|
template<typename Derived>
|
|
static HRESULT copyMWFlags(MCLCONVERSION_FLAGS, void *)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
};
|
|
|
|
|
|
template<typename Interface, typename Derived>
|
|
struct MarshalHelper
|
|
{
|
|
HRESULT copyMWFlags(MCLCONVERSION_FLAGS flags, void * receiver)
|
|
{
|
|
return MarshalHelperDelegate<Interface>::template copyMWFlags<Derived>(flags,receiver);
|
|
}
|
|
};
|
|
|
|
template<typename T, const IID* piid, typename T1, const CLSID* pclsid>
|
|
class CMCLMarshalImpl: public CMCLBaseImpl<T, piid, T1, pclsid>, public IMarshal, public MarshalHelper<T, T1>
|
|
{
|
|
public:
|
|
CMCLMarshalImpl()
|
|
{
|
|
m_serializedData = NULL;
|
|
}
|
|
|
|
// IUnknown
|
|
ULONG __stdcall AddRef()
|
|
{
|
|
return typename CMCLBaseImpl<T, piid, T1, pclsid>::AddRef();
|
|
}
|
|
ULONG __stdcall Release()
|
|
{
|
|
return typename CMCLBaseImpl<T, piid, T1, pclsid>::Release();
|
|
}
|
|
HRESULT __stdcall QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
if(riid == IID_IMarshal)
|
|
{
|
|
*ppv = static_cast<IMarshal*>(this);
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else
|
|
return typename CMCLBaseImpl<T, piid, T1, pclsid>::QueryInterface(riid, ppv);
|
|
}
|
|
|
|
// IMarshal
|
|
HRESULT __stdcall GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD dwFlags, CLSID* pClsid)
|
|
{
|
|
if(dwDestContext == MSHCTX_DIFFERENTMACHINE || dwDestContext == MSHCTX_INPROC)
|
|
{
|
|
IMarshal* pMarshal;
|
|
CoGetStandardMarshal(riid, (T*)pv, dwDestContext, pvDestContext, dwFlags, &pMarshal);
|
|
HRESULT hr = pMarshal->GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext, dwFlags, pClsid);
|
|
pMarshal->Release();
|
|
return hr;
|
|
}
|
|
*pClsid = *(const_cast<CLSID*>(pclsid));
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT __stdcall GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD dwFlags, DWORD* pSize)
|
|
{
|
|
if(dwDestContext == MSHCTX_DIFFERENTMACHINE || dwDestContext == MSHCTX_INPROC)
|
|
{
|
|
IMarshal* pMarshal;
|
|
CoGetStandardMarshal(riid, (T*)pv, dwDestContext, pvDestContext, dwFlags, &pMarshal);
|
|
HRESULT hr = pMarshal->GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext, dwFlags, pSize);
|
|
pMarshal->Release();
|
|
return hr;
|
|
}
|
|
//serialize the data the first time, this method is called twice during a marshalling operation
|
|
if(m_serializedData == NULL)
|
|
{
|
|
if(FAILED(Serialize(pv)))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
*pSize = static_cast<DWORD>(mxGetN(m_serializedData) + 4);
|
|
T1 * derivedObject = dynamic_cast<T1*>(this);
|
|
if(derivedObject->getCurrentMWFlags())
|
|
{
|
|
*pSize += sizeof(_MCLCONVERSION_FLAGS);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT __stdcall MarshalInterface(IStream* pStream, REFIID riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD dwFlags)
|
|
{
|
|
if(dwDestContext == MSHCTX_DIFFERENTMACHINE || dwDestContext == MSHCTX_INPROC)
|
|
{
|
|
IMarshal* pMarshal;
|
|
CoGetStandardMarshal(riid, (T*)pv, dwDestContext, pvDestContext, dwFlags, &pMarshal);
|
|
HRESULT hr = pMarshal->MarshalInterface(pStream, riid, pv, dwDestContext, pvDestContext, dwFlags);
|
|
pMarshal->Release();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD sizeOfPacket = static_cast<DWORD>(mxGetN(m_serializedData));
|
|
unsigned long bytesWritten = 0;
|
|
if(FAILED(hr = pStream->Write(&sizeOfPacket, 4, &bytesWritten))
|
|
|| bytesWritten != 4)
|
|
{
|
|
this->Error("Unable to write the size of the packet during serialization.");
|
|
return hr;
|
|
}
|
|
if(FAILED(hr = pStream->Write(mxGetData(m_serializedData), sizeOfPacket, &bytesWritten))
|
|
|| bytesWritten != sizeOfPacket)
|
|
{
|
|
this->Error("Unable to write the packet during serialization.");
|
|
return hr;
|
|
}
|
|
T1 * derivedObject = dynamic_cast<T1*>(this);
|
|
|
|
IMWFlags * mwflags;
|
|
if((mwflags = static_cast<IMWFlags*>(derivedObject->getCurrentMWFlags())) != NULL)
|
|
{
|
|
HRESULT hr;
|
|
_MCLCONVERSION_FLAGS flags;
|
|
if((hr = this->mclGetConversionFlags(mwflags,&flags)) != S_OK)
|
|
return hr;
|
|
if(FAILED(hr = pStream->Write(&flags, sizeof(_MCLCONVERSION_FLAGS), &bytesWritten))
|
|
|| bytesWritten != sizeof(_MCLCONVERSION_FLAGS))
|
|
{
|
|
this->Error("Unable to write the conversion flags during serialization.");
|
|
return hr;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT __stdcall DisconnectObject(DWORD dwReserved)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
HRESULT __stdcall UnmarshalInterface(IStream* pStream, REFIID riid, void** ppv)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD sizeOfPacket = 0;
|
|
unsigned long bytesRead = 0;
|
|
if(FAILED(hr = pStream->Read(&sizeOfPacket, 4, &bytesRead)))
|
|
{
|
|
this->Error("Unable to read the size of the packet during deserialization.");
|
|
return hr;
|
|
}
|
|
char * unmarshalledPacket = new char[sizeOfPacket];
|
|
|
|
if(FAILED(pStream->Read(unmarshalledPacket, sizeOfPacket, &bytesRead)))
|
|
{
|
|
this->Error("Unable to read the packet during deserialization.");
|
|
delete [] unmarshalledPacket;
|
|
return hr;
|
|
}
|
|
|
|
mxArray * unmarshalledMxArray = mclMxDeserialize(unmarshalledPacket, sizeOfPacket);
|
|
delete [] unmarshalledPacket;
|
|
|
|
_MCLCONVERSION_FLAGS flags;
|
|
this->mclInitConversionFlags(&flags);
|
|
VARIANT out;
|
|
VariantInit(&out);
|
|
if(this->mclmxArray2Variant(unmarshalledMxArray, &out, &flags) < 0)
|
|
{
|
|
this->Error("Deserialization error");
|
|
return E_FAIL;
|
|
}
|
|
if(unmarshalledMxArray != NULL)
|
|
mxDestroyArray(unmarshalledMxArray);
|
|
|
|
|
|
//Try to unmarshall MWFlags from pStream. This should only be true for data types
|
|
//which have a MWFlags and it was not NULL during marshalling.
|
|
hr = pStream->Read(&flags, sizeof(_MCLCONVERSION_FLAGS), &bytesRead);
|
|
if(!(hr == S_OK || hr == S_FALSE))
|
|
{
|
|
this->Error("Unable to read the conversion flags during deserialization.");
|
|
return hr;
|
|
}
|
|
|
|
//if unmarshalledMxArray == NULL, it means that the Variant type was default constructed
|
|
//but no value was assigned. So we can simply assign the this pointer to *ppv
|
|
if(unmarshalledMxArray != NULL)
|
|
{
|
|
if(bytesRead == sizeof(_MCLCONVERSION_FLAGS))
|
|
{
|
|
this->copyMWFlags(&flags, out.pdispVal);
|
|
}
|
|
*ppv = static_cast<T*>(out.pdispVal);
|
|
}
|
|
else
|
|
{
|
|
if(bytesRead == sizeof(_MCLCONVERSION_FLAGS))
|
|
{
|
|
copyMWFlags(&flags, this);
|
|
}
|
|
return QueryInterface(*piid, ppv);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT __stdcall ReleaseMarshalData(IStream* pStream)
|
|
{
|
|
if(m_serializedData != NULL)
|
|
mxDestroyArray(m_serializedData);
|
|
return S_OK;
|
|
}
|
|
|
|
private:
|
|
|
|
HRESULT __stdcall Serialize(void * pv)
|
|
{
|
|
VARIANT wrapper;
|
|
wrapper.vt = VT_DISPATCH;
|
|
wrapper.pdispVal = static_cast<IDispatch *>(pv);
|
|
mxArray * temp = NULL;
|
|
_MCLCONVERSION_FLAGS flags;
|
|
this->mclInitConversionFlags(&flags);
|
|
if(this->mclVariant2mxArray(&wrapper,&temp,&flags) < 0)
|
|
{
|
|
this->Error("Error in data conversion");
|
|
return E_FAIL;
|
|
}
|
|
|
|
m_serializedData = mclMxSerialize(temp);
|
|
if(temp != NULL)
|
|
mxDestroyArray(temp);
|
|
|
|
if(m_serializedData == NULL)
|
|
{
|
|
this->Error("Error in internal data serialization");
|
|
return E_FAIL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
private:
|
|
mxArray * m_serializedData;
|
|
};
|
|
|
|
|
|
#endif //ifdef __cplusplus
|
|
#endif //ifndef _MCLCOMCLASS_H_
|
|
|