#include "Sdk.h"
#include "SkypeComm.h"
#include "SkypeDefs.h"
#include "DbgTrace.h"
#include "Thread.h"
#include "Addin.h"
#include "MemMgr.h"
#include "StringMgr.h"
#include "StrUtils.h"
#include "_IDTExtensibility2.h"
#include "Addin.h"
static
const wchar_t *
kSkypeWatchWndClassName = L"OLNDesk.OutlookAddin:Windows:SkypeWatch";
// re-post since we need to be PostMessage, not SendMessage
static UINT OLNdesk_WM_COPYDATA = (UINT)-1;
static UINT OLNdesk_msg_SkypeControlAPIAttach = (UINT)-1;
const wchar_t* SKYPE_API_STATUS_string(SKYPE_API_STATUS status) {
switch(status) {
case SKYPECONTROLAPI_ATTACH_UNDEFINED:
return L"Unknown";
case SKYPECONTROLAPI_ATTACH_SUCCESS:
return L"Success";
case SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION:
return L"Authorization Pending";
case SKYPECONTROLAPI_ATTACH_REFUSED:
return L"Refused";
case SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE:
return L"Not available";
case SKYPECONTROLAPI_ATTACH_API_AVAILABLE:
return L"Available";
default:
return L"Unknown";
}
}
static
BOOL
GetAddinFromStream(IStream *pstmAddin,
IDispatch **ppDispAddin
) {
HRESULT hr = S_FALSE;
LARGE_INTEGER li = { 0 };
IUnknown *punkAddin = NULL;
BOOL Result = FALSE;
__try {
if(ppDispAddin == NULL) {
__leave;
}
*ppDispAddin = NULL;
if(pstmAddin == NULL) {
__leave;
}
hr = IStream_Seek(pstmAddin,
li,
STREAM_SEEK_SET,
NULL
);
if(FAILED(hr)) {
__leave;
}
hr = CoUnmarshalInterface(pstmAddin,
&IID_IDispatch,
(void **)ppDispAddin
);
if(FAILED(hr)) {
CoReleaseMarshalData(pstmAddin);
__leave;
}
Result = *ppDispAddin != NULL;
}
__finally {
if(punkAddin != NULL) {
IUnknown_Release(punkAddin);
}
}
return Result;
}
LRESULT CALLBACK
SkypeWatchWndProc(HWND hwnd,
UINT msg,
WPARAM wp,
LPARAM lp
) {
static UINT msg_SkypeControlAPIDiscover = 0;
static UINT msg_SkypeControlAPIAttach = 0;
static UINT_PTR ConnectTimerID = 0;
static SKYPE_API_STATUS APIStatus = SKYPECONTROLAPI_ATTACH_UNDEFINED;
XAddin *pAddin = NULL;
LRESULT Result = 0;
BOOL Handled = FALSE;
// we can access variables from pAddin,
// but we don't want to make calls into addin from a different thread
// if COM calls
if(OLNdesk_WM_COPYDATA == (UINT)-1) {
OLNdesk_WM_COPYDATA = RegisterWindowMessageW(L"OLNdesk_WM_COPYDATA");
}
if(OLNdesk_msg_SkypeControlAPIAttach == (UINT)-1) {
OLNdesk_msg_SkypeControlAPIAttach = RegisterWindowMessageW(L"OLNdesk_msg_SkypeControlAPIAttach");
}
if(msg == WM_NCCREATE) {
SetPropW(hwnd,
L"Addin",
(HANDLE)
(
(XAddin *)
(
((Thread *)(((((LPCREATESTRUCTW)lp)->lpCreateParams))))->ThreadParam
)
)
);
}
else if (msg == WM_CLOSE) {
PostQuitMessage(0);
return 0;
}
// this is the STA 0 pAddin object; can't invoke COM direct or indirect calls on this thread
pAddin = (XAddin *)GetPropW(hwnd, L"Addin");
if(msg == WM_CREATE) {
// start connection process with Skype
ConnectTimerID = SetTimer(hwnd, 1, 1000, NULL);
msg_SkypeControlAPIDiscover = RegisterWindowMessageW(L"SkypeControlAPIDiscover");
msg_SkypeControlAPIAttach = RegisterWindowMessageW(L"SkypeControlAPIAttach");
DTrace3(TRACE_LEVEL_DEBUG,
L"SkypeWatch/WM_CREATE ConnectTimerID=%ld msg_SkypeControlAPIDiscover=%ld msg_SkypeControlAPIAttach=%ld",
(UINT)ConnectTimerID,
msg_SkypeControlAPIDiscover,
msg_SkypeControlAPIAttach
);
}
else if(msg == WM_TIMER) {
if(wp == ConnectTimerID) {
LRESULT discoverResult;
if(APIStatus == SKYPECONTROLAPI_ATTACH_UNDEFINED
|| (pAddin->SkypeInfo.ApiStatus == SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE)) {
if(!pAddin->IsSkypeRunning(pAddin)) {
APIStatus = SKYPECONTROLAPI_ATTACH_UNDEFINED;
PostMessage(hwnd, OLNdesk_msg_SkypeControlAPIAttach, 0, (WPARAM)SKYPECONTROLAPI_ATTACH_UNDEFINED);
}
}
if(APIStatus == SKYPECONTROLAPI_ATTACH_UNDEFINED
|| (pAddin->SkypeInfo.ApiStatus == SKYPECONTROLAPI_ATTACH_API_AVAILABLE && pAddin->SkypeInfo.ApiHWND == NULL)) {
#pragma warning(disable:4306)
discoverResult = SendMessageTimeoutW(HWND_BROADCAST,
msg_SkypeControlAPIDiscover,
(WPARAM)hwnd,
0,
SMTO_ABORTIFHUNG,
1000,
NULL
);
#pragma warning(default:4306)
if(APIStatus != SKYPECONTROLAPI_ATTACH_UNDEFINED) {
DTrace4(TRACE_LEVEL_DEBUG,
L"SkypeWatch/WM_TIMER timerID=%ld discoverResult=%ld APIStatus=%ld (%s)",
(UINT)ConnectTimerID,
discoverResult,
APIStatus, SKYPE_API_STATUS_string((SKYPE_API_STATUS)APIStatus)
);
}
}
else if(pAddin->SkypeInfo.ApiHWND != NULL) {
if(!IsWindow(pAddin->SkypeInfo.ApiHWND)) {
DTrace3(TRACE_LEVEL_DEBUG,
L"SkypeWatch/WM_TIMER APIStatus=%ld (%s) ApiHWND=0x%p is not a valid HWND anymore. Skype has quit.",
APIStatus, SKYPE_API_STATUS_string((SKYPE_API_STATUS)APIStatus),
pAddin->SkypeInfo.ApiHWND
);
#if 0
pAddin->SetSkypeApiStatus(pAddin, SKYPECONTROLAPI_ATTACH_UNDEFINED);
#else
APIStatus = SKYPECONTROLAPI_ATTACH_UNDEFINED;
PostMessage(hwnd, OLNdesk_msg_SkypeControlAPIAttach, 0, (WPARAM)SKYPECONTROLAPI_ATTACH_UNDEFINED);
#endif
}
}
}
}
else if(msg == msg_SkypeControlAPIAttach) {
// skype answer to discover
APIStatus = (SKYPE_API_STATUS)lp;
DTrace3(TRACE_LEVEL_DEBUG,
L"SkypeWatch/msg_SkypeControlAPIAttach=%ld APIStatus=%ld (%ls)",
msg_SkypeControlAPIAttach,
APIStatus,
SKYPE_API_STATUS_string(APIStatus)
);
switch(APIStatus) {
case SKYPECONTROLAPI_ATTACH_SUCCESS:
DTrace0(TRACE_LEVEL_DEBUG,
L"SKYPECONTROLAPI_ATTACH_SUCCESS. Attach successful, can start sending commands."
);
// this is not a MTA call, just set an internal property of pAddin
pAddin->SetSkypeApiHWND(pAddin, (HWND)wp);
break;
case SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION:
DTrace0(TRACE_LEVEL_DEBUG,
L"SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION. Waiting for authorization."
);
break;
case SKYPECONTROLAPI_ATTACH_REFUSED:
DTrace0(TRACE_LEVEL_DEBUG,
L"SKYPECONTROLAPI_ATTACH_REFUSED. User denied access to Skype."
);
break;
case SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE:
DTrace0(TRACE_LEVEL_DEBUG,
L"SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE. API is not available."
);
break;
case SKYPECONTROLAPI_ATTACH_API_AVAILABLE:
DTrace0(TRACE_LEVEL_DEBUG,
L"SKYPECONTROLAPI_ATTACH_API_AVAILABLE. API has becoming available."
);
break;
default:
break;
}
// MTA call; invokes ribbon invalidate
#if 0
pAddin->SetSkypeApiStatus(pAddin, APIStatus);
#else
PostMessage(hwnd, OLNdesk_msg_SkypeControlAPIAttach, wp, lp);
#endif
Result = (LRESULT)TRUE;
Handled = TRUE;
}
else if(msg == OLNdesk_msg_SkypeControlAPIAttach) {
// invoke interthread call on pAddin
IDispatch *pDispAddin = NULL;
DISPPARAMS dp = {0};
VARIANT var;
HRESULT hr = S_FALSE;
int ps__ = 0;
__try {
VariantInit(&var);
if(!GetAddinFromStream(pAddin->pstmAddin, &pDispAddin)) {
__leave;
}
V_VT(&var) = VT_I4;
V_I4(&var) = (long)APIStatus;
dp.cArgs = 1;
dp.rgvarg = &var;
hr = IDispatch_Invoke(pDispAddin,
DISPID_ADDIN_SETAPISTATUS,
&IID_NULL,
0,
DISPATCH_METHOD,
&dp,
NULL,
NULL,
NULL
);
if(FAILED(hr)) {
__leave;
}
ps__ = 0;
}
__finally {
if(pDispAddin != NULL) {
IDispatch_Release(pDispAddin);
}
VariantClear(&var);
}
return 0;
}
else if(msg == WM_COPYDATA) {
HWND hwndSender = (HWND)wp;
PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT)lp;
DWORD_PTR Context = 0;
if(pcds != NULL) {
// reput the message, but as PostMessage
PCOPYDATASTRUCT_CTX pPostCDS = NULL;
// retrieve call context
Context = (DWORD_PTR)GetPropW(hwnd, L"SkypeCall.Context");
SetPropW(pAddin->SkypeWatchThread->ThreadHWND,
L"SkypeCall.Context",
(HANDLE)(0)
);
Allocator_AllocRef(sizeof(COPYDATASTRUCT_CTX),
&pPostCDS
);
if(pPostCDS != NULL) {
pPostCDS->dwContext = Context;
pPostCDS->dwData = pcds->dwData;
pPostCDS->cbData = pcds->cbData;
if(pcds->cbData != 0) {
Allocator_AllocRef(pcds->cbData,
&pPostCDS->lpData
);
if(pPostCDS->lpData == NULL) {
Allocator_FreeRef(&pPostCDS);
}
else {
memcpy(pPostCDS->lpData, pcds->lpData, pcds->cbData);
PostMessage(hwnd, OLNdesk_WM_COPYDATA, (WPARAM)hwndSender, (LPARAM)pPostCDS);
}
}
}
}
Result = (LRESULT)TRUE;
Handled = TRUE;
}
else if(msg == OLNdesk_WM_COPYDATA) {
HWND hwndSender = (HWND)wp;
PCOPYDATASTRUCT_CTX pcds = (PCOPYDATASTRUCT_CTX)lp;
if(pcds == NULL) {
DTrace1(TRACE_LEVEL_DEBUG,
L"SkypeWatch/WM_COPYDATA hwndSender=0x%p pcds is NULL",
hwndSender
);
}
else {
DWORD_PTR Context = pcds->dwContext;
#if 0
{
LPWSTR lpDataW = NULL;
StrUtf8ToUnicode(&lpDataW,
(const BYTE*)pcds->lpData,
-1
);
DTrace3(TRACE_LEVEL_DEBUG,
L"SkypeWatch/WM_COPYDATA hwndSender=0x%p pcds->dwContext=%ld pcds->lpData=%s",
hwndSender,
pcds->dwContext,
lpDataW
);
Allocator_FreeRef(&lpDataW);
}
#endif
// process the message from Skype
if(pAddin->SkypeInfo.ApiHWND != NULL
&& hwndSender == pAddin->SkypeInfo.ApiHWND) {
// MTA call
// cannot call pAddin->ProcessSkypeMessage(pAddin, pcds);
// invoke interthread call on pAddin
IDispatch *pDispAddin = NULL;
DISPPARAMS dp = {0};
VARIANT var[2];
SAFEARRAYBOUND rgsa[1];
void *pData = NULL;
HRESULT hr = S_FALSE;
int ps__ = 0;
__try {
VariantInit(&var[0]);
VariantInit(&var[1]);
if(!GetAddinFromStream(pAddin->pstmAddin, &pDispAddin)) {
__leave;
}
V_VT(&var[0]) = VT_I4;
V_I4(&var[0]) = (LONG)Context;
V_VT(&var[1]) = VT_UI1 | VT_ARRAY;
rgsa[0].cElements = pcds->cbData;
rgsa[0].lLbound = 0;
var[1].parray = SafeArrayCreate(VT_UI1, 1, rgsa);
if(var[1].parray == NULL) {
__leave;
}
hr = SafeArrayAccessData(var[1].parray, &pData);
memcpy(pData, pcds->lpData, pcds->cbData);
hr = SafeArrayUnaccessData(var[1].parray);
dp.cArgs = 2;
dp.rgvarg = &var[0];
hr = IDispatch_Invoke(pDispAddin,
DISPID_ADDIN_PROCESSSKYPEMESSAGE,
&IID_NULL,
0,
DISPATCH_METHOD,
&dp,
NULL,
NULL,
NULL
);
if(FAILED(hr)) {
__leave;
}
ps__ = 0;
}
__finally {
if(pDispAddin != NULL) {
IDispatch_Release(pDispAddin);
}
VariantClear(&var[0]);
VariantClear(&var[1]);
}
}
Allocator_FreeRef(&pcds->lpData);
Allocator_FreeRef(&pcds);
}
Result = (LRESULT)TRUE;
Handled = TRUE;
}
/*
if(piAddin != NULL) {
IDTExtensibility2_Release(piAddin);
}
*/
if(Handled) {
return Result;
}
return DefWindowProcW(hwnd, msg, wp, lp);
}
UINT
__stdcall
SkypeWatch(Thread *pThread) {
BOOL fShouldEnd = FALSE;
WNDCLASSEXW wc = {0};
HWND hwndSkypeWatch = NULL;
MSG msg = {0};
UINT Result = 0;
__try {
if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) {
Result = (UINT)-1;
__leave;
}
if(pThread == NULL) {
Result = (UINT)-1;
__leave;
}
// create listener/IPC window
wc.cbSize = sizeof(wc);
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = NULL;
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hIconSm = NULL;
wc.hInstance = NULL;
wc.lpfnWndProc = SkypeWatchWndProc;
wc.lpszClassName = kSkypeWatchWndClassName;
wc.lpszMenuName = NULL;
wc.style = 0;
if(RegisterClassExW(&wc) == 0) {
Result = (UINT)-2;
__leave;
}
hwndSkypeWatch = CreateWindowExW(0,
kSkypeWatchWndClassName,
L"OLNDesk.OutlookAddin.SkypeWatchWindow",
WS_POPUP,
-1,
-1,
0,
0,
NULL,
NULL,
NULL,
(LPVOID)pThread
);
if(hwndSkypeWatch == NULL) {
Result = (UINT)-3;
__leave;
}
pThread->ThreadHWND = hwndSkypeWatch;
ShowWindow(hwndSkypeWatch, SW_HIDE);
UpdateWindow(hwndSkypeWatch);
while(!fShouldEnd) {
// consume messages
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if(msg.message == WM_QUIT) {
DTrace0(TRACE_LEVEL_DEBUG,
L"SkypeWatch/WM_QUIT received"
);
fShouldEnd = TRUE;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if(fShouldEnd) {
break;
}
// check for end event
if(MsgWaitForMultipleObjects(1,
&pThread->SignalEndEvent,
FALSE,
INFINITE,
QS_ALLINPUT
) == WAIT_OBJECT_0) {
DestroyWindow(hwndSkypeWatch);
#if 0
PostThreadMessageW(GetCurrentThreadId(),
WM_QUIT,
0,
0
);
#else
PostQuitMessage(0);
#endif
}
}
Result = (UINT)msg.wParam;
}
__finally {
CoUninitialize();
}
return Result;
}