📄 sample.cpp
字号:
/**************************************************************************
* *
* Light OPC Server development library *
* *
* Copyright (c) 2000 Timofei Bondarenko *
*
Sample server
NOTE: the sample contains code for both in-proc and out-of-proc
servers. Therefore here is some overhead due this.
NOTE: the exe version of sample can be built for CONSOLE or WINDOWS mode.
see linker options for subsystem/entrypoint.
In console mode it will print messages to console window, when invoked
from command line, and can be terminated by Ctrl^C/Ctrl^Break.
On another hand, it will display a console window for a short time when
invoked from a windowed application.
**************************************************************************/
#define _WIN32_DCOM
#include <windows.h>
#include "unilog.h"
#define LOGID log,0
#include <ole2.h>
#include <olectl.h>
#include <oleauto.h>
#include <process.h>
#include <stdio.h>
#include <errno.h>
#include <locale.h>
#include <opcda.h>
#include <opcerror.h>
#include "lightopc.h"
#if 0
static LONG server_process;
#define CoAddRefServerProcess() (InterlockedIncrement(&server_process))
#define CoReleaseServerProcess() (InterlockedDecrement(&server_process))
#define CoQueryClientBlanket(a,b,c,d,e,userid,f) (*((const wchar_t**)userid)=L"Wine!")
#endif
unilog *log; /* logging entry */
int use_console = 0;
int test_mode = 0;
/********************* OPC vendor info ***************************************/
static const loVendorInfo vendor = {
3 /*Major */ , 2 /*Minor */ , 1 /*Build */ , 0 /*Reserv */ ,
"Sample OPC Server #9"
};
loService *my_service;
static int driver_init(int lflags);
static void driver_destroy(void);
static void simulate(unsigned pause);
/* OLE-specefic data: ***********************************************************/
// {C896CBD0-ABF5-11d4-BED0-00002120DB5C} inproc-server
static const GUID CLSID_LightOPCServerDLL =
{ 0xc896cbd0, 0xabf5, 0x11d4, {0xbe, 0xd0, 0x0, 0x0, 0x21, 0x20, 0xdb,
0x5c} };
// {4EA2713D-CA07-11d4-BEF5-00002120DB5C} exe-server
static const GUID CLSID_LightOPCServerEXE =
{ 0x4ea2713d, 0xca07, 0x11d4, {0xbe, 0xf5, 0x0, 0x0, 0x21, 0x20, 0xdb,
0x5c} };
/**** Server Counting stuff & OLE ICF implementation *****************************
The IClassFactory is unavoidable evil. Feel free to go ahead.
Basically we've to unload when the server_count being zero.
But there are different techniques for in-/out-of- proc servers.
*/
class myClassFactory: public IClassFactory
{
public:
int is_out_of_proc,
server_inuse; /* go 0 when unloading initiated */
LONG server_count;
CRITICAL_SECTION lk_count; /* protect server_count */
myClassFactory(): is_out_of_proc(0), server_inuse(0), server_count(0)
{
InitializeCriticalSection(&lk_count);
}
~myClassFactory()
{
DeleteCriticalSection(&lk_count);
}
void serverAdd(void);
void serverRemove(void);
/* Do nothing: we're static, he-he */
STDMETHODIMP_(ULONG) AddRef(void) { return 1; }
STDMETHODIMP_(ULONG) Release(void) { return 1; }
STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppInterface)
{
if (ppInterface == NULL)
return E_INVALIDARG;
if (iid == IID_IUnknown || iid == IID_IClassFactory)
{
UL_DEBUG((LOGID, "myClassFactory::QueryInterface() Ok"));
*ppInterface = this;
AddRef();
return S_OK;
}
UL_DEBUG((LOGID, "myClassFactory::QueryInterface() Failed"));
*ppInterface = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP LockServer(BOOL fLock)
{
UL_DEBUG((LOGID, "myClassFactory::LockServer(%d)", fLock));
if (fLock) serverAdd();
else serverRemove();
return S_OK;
}
STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid,
LPVOID *ppvObject);
};
static myClassFactory my_CF;
void myClassFactory::serverAdd(void)
{
EnterCriticalSection(&lk_count);
if (is_out_of_proc) CoAddRefServerProcess();
++server_count;
LeaveCriticalSection(&lk_count);
}
void myClassFactory::serverRemove(void)
{
EnterCriticalSection(&lk_count);
if (is_out_of_proc)
{
if (0 == CoReleaseServerProcess())
server_inuse = 0;
}
if (0 == --server_count && server_inuse) server_inuse = 0;
LeaveCriticalSection(&lk_count);
}
static void a_server_finished(void *arg, loService *b, loClient *c)
{
/* ARG is the same as we've passed to loClientCreate() */
UL_DEBUG((LOGID, "a_server_finished(%lu)...", my_CF.server_count));
my_CF.serverRemove();
/* OPTIONAL: */
UL_INFO((LOGID, "a_server_finished(%lu) USERID=<%ls>",
my_CF.server_count, arg? arg: L"<null>"));
if (use_console)
printf("-Detaching a client. USERID: <%ls>\n", arg? arg: L"<null>");
}
static void dll_simulator_wait(void); /* in-proc specefic */
static void dll_simulator_start(void); /* in-proc specefic */
STDMETHODIMP myClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid,
LPVOID *ppvObject)
{
IUnknown *server = 0;
HRESULT hr = S_OK;
/* OPTIONAL: security stuff */
OLECHAR *userid = 0;
wchar_t *cuserid;
LONG userno;
static LONG usercount;
userno = InterlockedIncrement(&usercount);
CoQueryClientBlanket(0, 0, 0, 0, 0, (RPC_AUTHZ_HANDLE*)&userid, 0);
if (!userid) userid = L"{unknown}";
UL_WARNING((LOGID, "USER:#%ld <%ls>", userno, userid));
if (use_console)
printf("+Attaching a client #%ld. USERID: <%ls>\n", usercount, userid);
if (cuserid = (wchar_t*)malloc((wcslen(userid) + 16) * sizeof(wchar_t)))
swprintf(cuserid, L"#%ld %ls", userno, userid);
/* -- end of security stuff */
if (pUnkOuter)
{
#if 1 /* Do we support aggregation? */
if (riid != IID_IUnknown)
#endif
return CLASS_E_NOAGGREGATION;
}
serverAdd(); /* the lock for a_server_finished() */
/***********************************************
* check other conditions (i.e. security) here *
***********************************************/
if (0 == server_inuse) /* we're stopped */
{ /* stopping initiated when there are no active instances */
if (is_out_of_proc)
{ /* a stopped EXE should exit soon and can't work anymore */
UL_MESSAGE((LOGID, "myClassFactory:: Server already finished"));
serverRemove();
return E_FAIL;
}
else /* in-proc specefic here: */
{ /* restart the server if not already running */
dll_simulator_wait(); /* wait for stopping complete */
dll_simulator_start(); /* then restart */
if (0 == server_inuse)
{
UL_MESSAGE((LOGID, "myClassFactory:: Can't start server"));
serverRemove();
return E_FAIL;
}
}
}
{
IUnknown *inner = 0;
if (loClientCreate_agg(my_service, (loClient**)&server,
pUnkOuter, &inner,
0, &vendor, a_server_finished, cuserid/*this*/))
{
serverRemove();
hr = E_OUTOFMEMORY;
UL_MESSAGE((LOGID, "myClassFactory::loClientCreate_agg() failed"));
}
else if (pUnkOuter) *ppvObject = (void*)inner; /*aggregation requested*/
else /* no aggregation */
{
/* loClientCreate(my_service, (loClient**)&server,
0, &vendor, a_server_finished, cuserid) - with no aggregation */
/* Initally the created SERVER has RefCount=1 */
hr = server->QueryInterface(riid, ppvObject); /* Then 2 (if success) */
server->Release(); /* Then 1 (on behalf of client) or 0 (if QI failed) */
if (FAILED(hr))
UL_MESSAGE((LOGID, "myClassFactory::loClient QueryInterface() failed"));
/* So we shouldn't carry about SERVER destruction at this point */
}
}
if (SUCCEEDED(hr))
{
loSetState(my_service, (loClient*)server,
loOP_OPERATE, (int)OPC_STATUS_RUNNING, /* other states are possible */
"Finished by client");
UL_DEBUG((LOGID, "myClassFactory::server_count = %ld", server_count));
}
return hr;
}
/************************* The END of OLE-specific ***************************/
/****************** The Process Data to be exported via OPC ******************
(driver's internal representation) */
static CRITICAL_SECTION lk_values; /* protects ti[] from simultaneous
access by simulator() and WriteTags() */
/* Our data tags: */
/* zero is resierved for an invalid RealTag */
#define TI_zuzu (1)
#define TI_lulu (2)
#define TI_bandwidth (3)
#define TI_array (4)
#define TI_enum (5)
#define TI_quiet (6)
#define TI_quality (7)
#define TI_string (8)
#define TI_MAX (8)
static loTagId ti[TI_MAX + 1]; /* their IDs */
static const char *tn[TI_MAX + 1] = /* their names */
{ "--not--used--", "zuzu", "lulu", "bandwidth", "array", "enum-localizable",
"quiet", "quality", "string" };
static loTagValue tv[TI_MAX + 1]; /* their values */
/**********************************************************************
sample server initiation & simulation
**********************************************************************/
/* OPTIONAL: show client's interests */
void activation_monitor(const loCaller *ca, int count, loTagPair *til)
{
int act = 1;
if (0 > count)
act = 0, count = -count;
while(count--)
{
UL_DEBUG((LOGID, "MON: %u %s %s", til[count].tpTi,
tn[(int) til[count].tpRt], act ? "On" : "Off"));
}
}
/* OPTIONAL: write back to the device */
int WriteTags(const loCaller *ca,
unsigned count, loTagPair taglist[],
VARIANT values[], HRESULT error[], HRESULT *master, LCID lcid)
{
unsigned ii;
UL_TRACE((LOGID, "WriteTags(%x) invoked"));
EnterCriticalSection(&lk_values);
for(ii = 0; ii < count; ii++)
{
HRESULT hr = S_OK;
loTagId clean = 0;
switch((int)taglist[ii].tpRt)
{
case TI_zuzu:
{ double oldval = tv[TI_zuzu].tvValue.dblVal;
hr = VariantChangeType(&tv[TI_zuzu].tvValue,
&values[ii], 0, V_VT(&tv[TI_zuzu].tvValue));
UL_INFO((LOGID, "%p:%s:WriteTags(zuzu) = %.1f old=%.1f [%x]",
ca->ca_cli, loClientName(ca->ca_cli),
tv[TI_zuzu].tvValue.dblVal, oldval, hr));
if (tv[TI_zuzu].tvValue.dblVal < 0)
hr = DISP_E_TYPEMISMATCH; /* simulate a write error */
}
break;
case TI_lulu:
hr = VariantChangeType(&tv[TI_lulu].tvValue, &values[ii], 0, VT_I2);
if (S_OK == hr)
lo_statperiod(V_I2(&tv[TI_lulu].tvValue)); /* VERY OPTIONAL, really */
/* The splining factor for bandwidth calculations/trending */
break;
case TI_quiet:
hr = VariantChangeType(&tv[TI_quiet].tvValue, &values[ii], 0,
V_VT(&tv[TI_quiet].tvValue));
break;
case TI_array:
hr = lo_variant_changetype_array(&tv[TI_array].tvValue,
&values[ii], lcid, 0,
V_VT(&tv[TI_array].tvValue));
break;
case 0: /* ignore */
default: /* preserve unhandled tags */
clean = taglist[ii].tpTi;
break;
}
if (S_OK != hr)
{
*master = S_FALSE;
error[ii] = hr;
UL_TRACE((LOGID, "%!l WriteTags(Rt=%u Ti=%u %s)",
hr, taglist[ii].tpRt, taglist[ii].tpTi,
tn[(int)taglist[ii].tpRt <= TI_MAX? (int)taglist[ii].tpRt: 0]));
}
taglist[ii].tpTi = clean; /* clean if ok */
}
LeaveCriticalSection(&lk_values);
return loDW_TOCACHE; /* put to the cache all tags unhandled here */
// loDW_ALLDONE;
}
/* OPTIONAL: example of non-trivial datatype conversion */
static void local_text(WCHAR buf[32], unsigned nn, LCID lcid);
void ConvertTags(const loCaller *ca,
unsigned count, const loTagPair taglist[],
VARIANT *values, WORD *qualities, HRESULT *errs,
HRESULT *master_err, HRESULT *master_qual,
const VARIANT src[], const VARTYPE vtypes[], LCID lcid)
{
unsigned ii;
for(ii = 0; ii < count; ii++)
{
HRESULT hr = S_OK;
VARTYPE reqtype = vtypes[ii];
if (reqtype == VT_EMPTY) reqtype = V_VT(&src[ii]);
switch((int)taglist[ii].tpRt)
{
case 0: /* ignore */
break;
case TI_array:
if (V_VT(&src[ii]) == reqtype)
{
hr = values == src? S_OK : VariantCopy(&values[ii], (VARIANT*)&src[ii]);
}
else
{
hr = lo_variant_changetype_array(&values[ii],
(VARIANT*)&src[ii], lcid, 0, reqtype);
}
break;
case TI_enum:
if (V_VT(&src[ii]) == VT_I2 && reqtype == VT_BSTR)
{
WCHAR ww[32];
int vv = V_I2(&src[ii]);
VariantClear(&values[ii]);
local_text(ww, vv, lcid);
if (V_BSTR(&values[ii]) = SysAllocString(ww))
V_VT(&values[ii]) = VT_BSTR;
else hr = E_OUTOFMEMORY;
break;
}
default:
if (reqtype == V_VT(&src[ii]))
{
if (values == src) hr = S_OK;
else hr = VariantCopy(values + ii, (VARIANT*)&src[ii]);
}
else
hr = VariantChangeType(values + ii, (VARIANT*)&src[ii], 0, reqtype);
UL_ERROR((LOGID, "ConvII NotRequested(%u %u)[%u/%u]",
taglist[ii].tpRt, taglist[ii].tpTi, ii, count));
break;
} /* end of switch( */
if (S_OK != hr)
{
errs[ii] = hr;
qualities[ii] = OPC_QUALITY_BAD;
*master_err = *master_qual = S_FALSE;
UL_WARNING((LOGID, "%!l ConvII Error(Rt=%u Ti=%u %s)[%u/%u]",
hr, taglist[ii].tpRt, taglist[ii].tpTi,
tn[(int)taglist[ii].tpRt <= TI_MAX? (int)taglist[ii].tpRt: 0],
ii, count));
}
} /* end of for(...*/
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -