Windows Service communicating with Browser Windows?

G

Guest

I would like to write a Windows Service that can communicate with any open
browser windows that an interactive user may be running. The service itself
does not need to have a UI.

The company that I work for develops software that monitors Customer Service
Representative (CSR) performance. When you call a help desk and hear "This
call may be monitored for quality purposes", the software that does this
monitoring very likely has been developed by my company. One of the
configurable triggers that may start a recording is what we term a "URL
Trigger". Recording will be started if a CSR navigates to a targeted web site.

Currently, URL Triggers are implemented via a Layered Service Provider (LSP)
which sits on top of the TCP/IP protocol stack, monitoring the flow of bytes
across HTTP port 80. LSPs have historically had many incompatibility
problems, and I had previously received much-appreciated help from these
newsgroups resolving these issues. My last big change was porting our code
from a non-IFS LSP to an IFS LSP, but that's another story.

We have recently run into several major dead ends with the LSP approach, and
we would like instead to be able to be able to query the IE browser windows
to find out what URLs they are currently focused on.

My initial naive approach to this problem (which I have since learned to be
wildly incorrect) was to access shdocvw.dll functions. My code (with debug
output removed and reformatted to use less space) follows after my signature.
I since learned that as a client type ActiveX component, shdocvw.dll was
never designed to be hosted from a service.

The "proper solution", I have learned, is to write a separate windowless
application running in the user space, that communicates with the service via
interprocess communications. Although simple to implement, this solution is
unacceptable to my manager. Remember the function of our software: to
eavesdrop on the CSR. A separate application running in the user desktop
could easily be switched off by an errant CSR whenever said CSR doesn't want
to be monitored, and then switched on again.

Can anybody suggest an alternative solution to what I am trying to accomplish?

Thanks,
Thomas Yee

This is the C++ code that doesn't work when started from a Windows Service:
DWORD WINAPI CQURLTriggerApp::URLTriggerThread(LPVOID lpParameter) {
try {
SHDocVw::IShellWindowsPtr pShellWindows;
BSTR_T_SET oldURLList, newURLList;
HRESULT hr;
char errmsg[256];

if (FAILED(hr = CoInitialize(NULL))) {
sprintf(errmsg, "[QURLTrigger] CoInitialize failed, reason =
0x%x", hr);
throw exception(errmsg);
}
if (FAILED(hr = CoCreateInstance(__uuidof(SHDocVw::ShellWindows),
NULL, CLSCTX_ALL,
IID_IShellWindows, (void**)&pShellWindows))) {
sprintf(errmsg, "[QURLTrigger] CoCreateInstance failed, reason =
0x%x", hr);
throw exception(errmsg);
}

while(m_running) {
long nCount = pShellWindows->GetCount();
newURLList.clear();
IDispatchPtr pDispatch;
for (long i = 0; i < nCount; i++) {
try {
_variant_t va(i, VT_I4);
pDispatch = pShellWindows->Item(va);
SHDocVw::IWebBrowser2Ptr pBrowser(pDispatch);
if (pBrowser) {
// Get the browser interface
SHDocVw::IWebBrowserApp *pBrowserApp;
if
(SUCCEEDED(pDispatch->QueryInterface(IID_IWebBrowserApp,
(void**)&pBrowserApp))) {
HWND hwndWebBrowserApp = 0;
if
(SUCCEEDED(pBrowserApp->get_HWND((LONG_PTR*)&hwndWebBrowserApp))) {
if (GetForegroundWindow()==
hwndWebBrowserApp) {

newURLList.insert(pBrowser->GetLocationURL());
}
}
pBrowserApp->Release();
}
else OutputDebugString("[QURLTrigger]
QueryInterface() failed");
}
else OutputDebugString("[QURLTrigger]
SHDocVw::IWebBrowser2Ptr constructor failed");
}
catch(...) {
OutputDebugString("[QURLTrigger] Unidentified error in
polling loop");
}
}
AlertNewURLs(oldURLList, newURLList);
oldURLList = newURLList;
Sleep(m_pollingInterval);
}
pShellWindows->Release();
CoUninitialize();
}
catch(exception &e) {
OutputDebugString(e.what());
}
catch(...) {
OutputDebugString("[QURLTrigger] Unknown error");
}
return 0;
}
 
M

Mike P

inline --

Tom Yee said:
I would like to write a Windows Service that can communicate with any open
browser windows that an interactive user may be running. The service
itself
does not need to have a UI.

The company that I work for develops software that monitors Customer
Service
Representative (CSR) performance. When you call a help desk and hear "This
call may be monitored for quality purposes", the software that does this
monitoring very likely has been developed by my company. One of the
configurable triggers that may start a recording is what we term a "URL
Trigger". Recording will be started if a CSR navigates to a targeted web
site.

Currently, URL Triggers are implemented via a Layered Service Provider
(LSP)
which sits on top of the TCP/IP protocol stack, monitoring the flow of
bytes
across HTTP port 80. LSPs have historically had many incompatibility
problems, and I had previously received much-appreciated help from these
newsgroups resolving these issues. My last big change was porting our code
from a non-IFS LSP to an IFS LSP, but that's another story.

We have recently run into several major dead ends with the LSP approach,
and
we would like instead to be able to be able to query the IE browser
windows
to find out what URLs they are currently focused on.

My initial naive approach to this problem (which I have since learned to
be
wildly incorrect) was to access shdocvw.dll functions. My code (with debug
output removed and reformatted to use less space) follows after my
signature.
I since learned that as a client type ActiveX component, shdocvw.dll was
never designed to be hosted from a service.

The "proper solution", I have learned, is to write a separate windowless
application running in the user space, that communicates with the service
via
interprocess communications. Although simple to implement, this solution
is
unacceptable to my manager. Remember the function of our software: to
eavesdrop on the CSR. A separate application running in the user desktop
could easily be switched off by an errant CSR whenever said CSR doesn't
want
to be monitored, and then switched on again.

I'm not sure what you mean by 'switched off' regarding an exe --- if it's
meant as Terminate Process, there are probably a couple of ways to detect
that. I've done a similar scenerio having a service launch a user mode COM
server using DCOM with connection points. If the user kills the COM server,
they have no way to restart it. And the service will notice that the COM
server has dissappeared.

Mike P
Can anybody suggest an alternative solution to what I am trying to
accomplish?

Thanks,
Thomas Yee

This is the C++ code that doesn't work when started from a Windows
Service:
DWORD WINAPI CQURLTriggerApp::URLTriggerThread(LPVOID lpParameter) {
try {
SHDocVw::IShellWindowsPtr pShellWindows;
BSTR_T_SET oldURLList, newURLList;
HRESULT hr;
char errmsg[256];

if (FAILED(hr = CoInitialize(NULL))) {
sprintf(errmsg, "[QURLTrigger] CoInitialize failed, reason =
0x%x", hr);
throw exception(errmsg);
}
if (FAILED(hr = CoCreateInstance(__uuidof(SHDocVw::ShellWindows),
NULL, CLSCTX_ALL,
IID_IShellWindows, (void**)&pShellWindows))) {
sprintf(errmsg, "[QURLTrigger] CoCreateInstance failed, reason
=
0x%x", hr);
throw exception(errmsg);
}

while(m_running) {
long nCount = pShellWindows->GetCount();
newURLList.clear();
IDispatchPtr pDispatch;
for (long i = 0; i < nCount; i++) {
try {
_variant_t va(i, VT_I4);
pDispatch = pShellWindows->Item(va);
SHDocVw::IWebBrowser2Ptr pBrowser(pDispatch);
if (pBrowser) {
// Get the browser interface
SHDocVw::IWebBrowserApp *pBrowserApp;
if
(SUCCEEDED(pDispatch->QueryInterface(IID_IWebBrowserApp,
(void**)&pBrowserApp))) {
HWND hwndWebBrowserApp = 0;
if
(SUCCEEDED(pBrowserApp->get_HWND((LONG_PTR*)&hwndWebBrowserApp))) {
if (GetForegroundWindow()==
hwndWebBrowserApp) {

newURLList.insert(pBrowser->GetLocationURL());
}
}
pBrowserApp->Release();
}
else OutputDebugString("[QURLTrigger]
QueryInterface() failed");
}
else OutputDebugString("[QURLTrigger]
SHDocVw::IWebBrowser2Ptr constructor failed");
}
catch(...) {
OutputDebugString("[QURLTrigger] Unidentified error in
polling loop");
}
}
AlertNewURLs(oldURLList, newURLList);
oldURLList = newURLList;
Sleep(m_pollingInterval);
}
pShellWindows->Release();
CoUninitialize();
}
catch(exception &e) {
OutputDebugString(e.what());
}
catch(...) {
OutputDebugString("[QURLTrigger] Unknown error");
}
return 0;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top