528 lines
13 KiB
C++
528 lines
13 KiB
C++
/*
|
|
|
|
copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ddeLink.cpp
|
|
|
|
Abstract:
|
|
|
|
This module implements:
|
|
DdeBindToObject
|
|
DdeIsRunning
|
|
|
|
Author:
|
|
|
|
Jason Fuller (jasonful) 19-October-1992
|
|
|
|
*/
|
|
#include "ddeproxy.h"
|
|
// #include <ctype.h>
|
|
|
|
|
|
INTERNAL DdeBindToObject
|
|
(LPCOLESTR szFileIn,
|
|
REFCLSID clsid,
|
|
BOOL fPackageLink,
|
|
REFIID iid,
|
|
LPLPVOID ppv)
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"DdeBindToObject szFileIn(%ws) fPackageLink(%x)\n",
|
|
szFileIn,
|
|
fPackageLink));
|
|
|
|
|
|
LPUNKNOWN punk;
|
|
*ppv = NULL;
|
|
CDdeObject FAR* pdde=NULL;
|
|
HRESULT hresult = E_UNEXPECTED;
|
|
BOOL fSysConnection = FALSE;
|
|
WCHAR wszTmpFile [MAX_STR+5];
|
|
|
|
//
|
|
// This protocol doesn't handle the fact that there are two names for
|
|
// every file. This is a bit of a problem. So, we are going to choose
|
|
// the short name as the one to look for. This means that DDE objects
|
|
// using the long filename will not work very well.
|
|
//
|
|
WCHAR szFile[MAX_PATH];
|
|
if ((lstrlenW(szFileIn) == 0) || (GetShortPathName(szFileIn,szFile,MAX_PATH) == 0))
|
|
{
|
|
//
|
|
// Unable to determine a short path for this object. Use whatever we were
|
|
// handed.
|
|
//
|
|
intrDebugOut((DEB_ITRACE,"No conversion for short path. Copy szFileIn\n"));
|
|
lstrcpyW(szFile,szFileIn);
|
|
}
|
|
intrDebugOut((DEB_ITRACE,"Short file szFile(%ws)\n",szFile));
|
|
|
|
RetZS (punk=CDdeObject::Create (NULL,clsid,OT_LINK,wGlobalAddAtom(szFile),
|
|
NULL,&pdde),E_OUTOFMEMORY);
|
|
RetZ (pdde);
|
|
|
|
// Document already running?
|
|
|
|
if (NOERROR != (hresult = pdde->DocumentLevelConnect (NULL) ))
|
|
{
|
|
if (GetScode (hresult) != S_FALSE)
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"DdeBindToObject szFile(%ws) DLC returns %x \n",
|
|
szFile,hresult));
|
|
goto exitRtn;
|
|
}
|
|
|
|
|
|
// If not already running, try to make a sys level connection
|
|
|
|
if (!pdde->m_pSysChannel) {
|
|
if (!pdde->AllocDdeChannel (&pdde->m_pSysChannel, SYS_CLASSA))
|
|
{
|
|
intrAssert( !"Out of memory");
|
|
hresult = E_OUTOFMEMORY;
|
|
goto exitRtn;
|
|
}
|
|
}
|
|
|
|
hresult = ReportResult (0, E_UNEXPECTED, 0, 0);
|
|
|
|
if (fPackageLink) {
|
|
lstrcpyW (wszTmpFile, szFile);
|
|
lstrcatW (wszTmpFile, L"/Link");
|
|
pdde->SetTopic (wGlobalAddAtom(wszTmpFile));
|
|
}
|
|
|
|
if (pdde->InitSysConv())
|
|
{
|
|
fSysConnection = TRUE;
|
|
|
|
// Try to make the server open the document
|
|
ErrRtnH (pdde->PostSysCommand (pdde->m_pSysChannel, (LPSTR)&achStdOpenDocument,FALSE));
|
|
pdde->m_fDidStdOpenDoc = TRUE;
|
|
|
|
}
|
|
else
|
|
{
|
|
// launch the server
|
|
if (!pdde->LaunchApp())
|
|
{
|
|
hresult = CO_E_APPNOTFOUND;
|
|
goto errRtn;
|
|
}
|
|
}
|
|
|
|
if (fPackageLink)
|
|
pdde->SetTopic (wGlobalAddAtom(szFile));
|
|
|
|
// Connect to document
|
|
hresult = pdde->m_ProxyMgr.Connect (IID_NULL, CLSID_NULL);
|
|
if (hresult != NOERROR)
|
|
{
|
|
// Excel does not register its document in time if it loads
|
|
// startup macros. So we force it to open the document.
|
|
if (pdde->InitSysConv())
|
|
{
|
|
fSysConnection = TRUE;
|
|
// Try to make the server open the document.
|
|
ErrRtnH (pdde->PostSysCommand (pdde->m_pSysChannel,
|
|
(LPSTR)&achStdOpenDocument,
|
|
FALSE));
|
|
pdde->m_fDidStdOpenDoc = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ErrRtnH (ResultFromScode (CO_E_APPDIDNTREG));
|
|
}
|
|
// Try connecting to document again. Should succeed.
|
|
hresult = pdde->m_ProxyMgr.Connect (IID_NULL, CLSID_NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Already running, so assume visible
|
|
pdde->DeclareVisibility (TRUE);
|
|
}
|
|
|
|
errRtn:
|
|
if (pdde->m_pSysChannel) {
|
|
if (fSysConnection)
|
|
pdde->TermConv (pdde->m_pSysChannel);
|
|
else
|
|
pdde->DeleteChannel (pdde->m_pSysChannel);
|
|
}
|
|
|
|
if (hresult == NOERROR) {
|
|
hresult = punk->QueryInterface (iid, ppv);
|
|
}
|
|
pdde->m_pUnkOuter->Release();
|
|
if (hresult!=NOERROR)
|
|
{
|
|
Warn ("DdeBindToObject failed");
|
|
}
|
|
|
|
exitRtn:
|
|
intrDebugOut((DEB_ITRACE,
|
|
"DdeBindToObject szFile(%ws) returns %x \n",
|
|
szFile,hresult));
|
|
|
|
return hresult;
|
|
}
|
|
|
|
|
|
//
|
|
// BUGBUG: This won't work in a multi-threaded world.
|
|
//
|
|
static LPOLESTR szOriginalUNCName;
|
|
static WCHAR cOriginalDrive;
|
|
|
|
static INTERNAL InitializeIterator
|
|
(LPCOLESTR wszFile)
|
|
{
|
|
WCHAR wszDrive[] = L"A:\\";
|
|
|
|
if ((wszFile == NULL) || (wszFile[1] != ':'))
|
|
{
|
|
return(S_FALSE);
|
|
}
|
|
|
|
wszDrive[0] = (WCHAR)CharUpperW((LPWSTR)wszFile[0]);
|
|
|
|
if (GetDriveType(wszDrive) == DRIVE_REMOTE)
|
|
{
|
|
|
|
DWORD cb = MAX_STR;
|
|
wszDrive[2] = '\0';
|
|
if (NULL==szOriginalUNCName)
|
|
{
|
|
szOriginalUNCName = new WCHAR [MAX_STR];
|
|
}
|
|
|
|
|
|
if (WN_SUCCESS == OleWNetGetConnection (wszDrive, szOriginalUNCName, &cb))
|
|
{
|
|
cOriginalDrive = (WCHAR)CharUpperW((LPWSTR)wszFile[0]);
|
|
return NOERROR;
|
|
}
|
|
}
|
|
// szFile is not a network file
|
|
return ReportResult (0, S_FALSE, 0, 0);
|
|
}
|
|
|
|
|
|
|
|
// NextEquivalentNetDrive
|
|
//
|
|
// Change the drive letter of szFile to the next (modulo 'Z') drive letter
|
|
// that is connected to the same net drive
|
|
// Return S_FALSE when there are no more equivalent drives
|
|
//
|
|
static INTERNAL NextEquivalentNetDrive
|
|
(LPOLESTR szFile)
|
|
{
|
|
#define incr(c) (c=='Z' ? c='A' : ++c)
|
|
WCHAR wszDrive[3]= L"A:";
|
|
Assert (szFile && szFile[1]==':');
|
|
|
|
char cDrive = (char)CharUpperW((LPWSTR)szFile[0]);
|
|
|
|
while (cOriginalDrive != incr(cDrive))
|
|
{
|
|
|
|
DWORD cb = MAX_PATH;
|
|
WCHAR szUNCName [MAX_PATH];
|
|
wszDrive[0] = cDrive;
|
|
|
|
Assert (cDrive >= 'A' && cDrive <= 'Z');
|
|
Assert (szOriginalUNCName);
|
|
|
|
if (WN_SUCCESS == OleWNetGetConnection (wszDrive,szUNCName, &cb) &&
|
|
(0 == lstrcmpW (szUNCName, szOriginalUNCName)))
|
|
{
|
|
szFile[0] = cDrive;
|
|
return NOERROR;
|
|
}
|
|
}
|
|
// We've gone through all the drives
|
|
return ReportResult (0, S_FALSE, 0, 0);
|
|
}
|
|
|
|
|
|
|
|
// Dde_IsRunning
|
|
//
|
|
// Attempt to open a document-level conversation using the
|
|
// filename as a topic. If the conversation is established we
|
|
// know the file is running and terminate the conversation.
|
|
// Otherwise it is not running.
|
|
//
|
|
INTERNAL DdeIsRunning
|
|
(CLSID clsid,
|
|
LPCOLESTR szFileIn,
|
|
LPBC pbc,
|
|
LPMONIKER pmkToLeft,
|
|
LPMONIKER pmkNewlyRunning)
|
|
{
|
|
intrDebugOut((DEB_ITRACE,
|
|
"DdeIsRunning szFileIn(%ws)\n",szFileIn));
|
|
|
|
ATOM aTopic;
|
|
CDdeObject FAR* pdde=NULL;
|
|
HRESULT hres = ReportResult(0, S_FALSE, 0, 0);
|
|
|
|
if (NULL==szFileIn || '\0'==szFileIn[0])
|
|
{
|
|
// A NULL filename is invalid for our purposes.
|
|
// But if we did a DDE_INITIATE, NULL would mean "any topic",
|
|
// and if we were called by RunningMoniker() with CLSID_NULL,
|
|
// then we would be INITIATEing on "any app, any topic" and
|
|
// SHELL (if not others) would respond.
|
|
intrDebugOut((DEB_ITRACE,
|
|
"DdeIsRunning NULL szFileIn\n"));
|
|
|
|
hres = S_FALSE;
|
|
goto exitRtn;
|
|
}
|
|
//
|
|
// This protocol doesn't handle the fact that there are two names for
|
|
// every file. This is a bit of a problem. So, we are going to choose
|
|
// the short name as the one to look for. This means that DDE objects
|
|
// using the long filename will not work very well.
|
|
//
|
|
WCHAR szFile[MAX_PATH];
|
|
if ((lstrlenW(szFileIn) == 0) || (GetShortPathName(szFileIn,szFile,MAX_PATH) == 0))
|
|
{
|
|
//
|
|
// Unable to determine a short path for this object. Use whatever we were
|
|
// handed.
|
|
//
|
|
intrDebugOut((DEB_ITRACE,"No conversion for short path. Copy szFileIn\n"));
|
|
lstrcpyW(szFile,szFileIn);
|
|
}
|
|
intrDebugOut((DEB_ITRACE,"Short file szFile(%ws)\n",szFile));
|
|
|
|
#ifdef KEVINRO_OLDCODE
|
|
|
|
We have removed SzFixNet from the system, since the file system is
|
|
capable of dealing with UNC names now. Therefore, I have removed the
|
|
following lines. I am pretty sure this is going to work, but I am
|
|
leaving this code here until we decide that it is a good decision.
|
|
If you find this, and I haven't removed it, chances are that I forgot
|
|
to remove this code.
|
|
HRESULT hresFixNet;
|
|
UINT dummy;
|
|
|
|
// If UNC name, change to drive letter
|
|
LPOLESTR szFile = NULL;
|
|
dummy = 0xFFFF;
|
|
|
|
hresFixNet = SzFixNet (pbc, szFile, &szFile, &dummy);
|
|
// SzFixNet may return NOERROR and szFile==NULL
|
|
if (szFile==NULL || hresFixNet != NOERROR)
|
|
{
|
|
aTopic = wGlobalAddAtom(szFile);
|
|
intrAssert(wIsValidAtom(aTopic));
|
|
}
|
|
else
|
|
{
|
|
aTopic = wGlobalAddAtom (szFile);
|
|
intrAssert(wIsValidAtom(aTopic));
|
|
delete szFile;
|
|
}
|
|
#else
|
|
aTopic = wGlobalAddAtom (szFile);
|
|
intrAssert(wIsValidAtom(aTopic));
|
|
#endif // KEVINRO_OLDCODE
|
|
|
|
|
|
ErrZ (CDdeObject::Create (NULL, clsid, OT_LINK, aTopic, NULL, &pdde));
|
|
|
|
if (NOERROR == pdde->DocumentLevelConnect (pbc))
|
|
{
|
|
// It is running!
|
|
// Immediately terminate conversation. We just wanted to know
|
|
// if it was running.
|
|
hres = NOERROR;
|
|
}
|
|
else
|
|
{
|
|
// Not running
|
|
hres = ReportResult(0, S_FALSE, 0, 0);
|
|
}
|
|
|
|
errRtn:
|
|
|
|
if (aTopic)
|
|
intrAssert(wIsValidAtom(aTopic));
|
|
GlobalDeleteAtom (aTopic);
|
|
if (pdde)
|
|
{
|
|
Assert (pdde->m_refs==1);
|
|
pdde->m_pUnkOuter->Release();
|
|
}
|
|
|
|
exitRtn:
|
|
intrDebugOut((DEB_ITRACE,
|
|
"DdeIsRunning szFile(%ws) returns %x\n",szFile,hres));
|
|
return hres;
|
|
}
|
|
|
|
|
|
#if 0
|
|
INTERNAL DdeIsRunning
|
|
(CLSID clsid,
|
|
LPCSTR cszFile,
|
|
LPBC pbc,
|
|
LPMONIKER pmkToLeft,
|
|
LPMONIKER pmkNewlyRunning)
|
|
{
|
|
HRESULT hresult = NOERROR;
|
|
LPSTR szFile = NULL;
|
|
|
|
// Normal case
|
|
if (NOERROR == Dde_IsRunning (clsid, cszFile, pbc, pmkToLeft,
|
|
pmkNewlyRunning))
|
|
{
|
|
return NOERROR;
|
|
}
|
|
|
|
if (cszFile[0]=='\\' && cszFile[1]=='\\')
|
|
{
|
|
RetErr (SzFixNet (pbc, (LPSTR)cszFile, &szFile));
|
|
// Try with a drive letter instead of a UNC name
|
|
if (NOERROR==Dde_IsRunning (clsid, szFile, pbc, pmkToLeft,
|
|
pmkNewlyRunning))
|
|
{
|
|
hresult = NOERROR;
|
|
goto errRtn;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
szFile = UtDupString (cszFile); // so it can be deleted
|
|
}
|
|
|
|
// If failure, see if the file is running under a different net
|
|
// drive letter that is mapped to the same drive.
|
|
|
|
if (InitializeIterator (szFile) != NOERROR)
|
|
{
|
|
// file is probably not on a network drive
|
|
hresult = ResultFromScode (S_FALSE);
|
|
goto errRtn;
|
|
}
|
|
|
|
while (NOERROR==NextEquivalentNetDrive (szFile))
|
|
{
|
|
if (NOERROR == Dde_IsRunning (clsid, szFile, pbc, pmkToLeft,
|
|
pmkNewlyRunning))
|
|
{
|
|
hresult = NOERROR;
|
|
goto errRtn;
|
|
}
|
|
}
|
|
// not running
|
|
hresult = ResultFromScode (S_FALSE);
|
|
|
|
errRtn:
|
|
delete szFile;
|
|
return hresult;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
// CDdeObject::DocumentLevelConnect
|
|
//
|
|
// Try to connect to document (m_aTopic) even if the document is running
|
|
// under a different drive letter that is mapped to the same network drive.
|
|
//
|
|
INTERNAL CDdeObject::DocumentLevelConnect
|
|
(LPBINDCTX pbc)
|
|
{
|
|
ATOM aOriginal;
|
|
ATOM aTopic;
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"CDdeObject::DocumentLevelConnect(%x)\n",this));
|
|
HRESULT hresult = NOERROR;
|
|
|
|
// Normal case
|
|
if (NOERROR==m_ProxyMgr.Connect (IID_NULL, CLSID_NULL))
|
|
{
|
|
goto exitRtn;
|
|
}
|
|
|
|
|
|
WCHAR szFile[MAX_STR];
|
|
WCHAR szUNCFile[MAX_STR];
|
|
|
|
Assert (wIsValidAtom (m_aTopic));
|
|
if (GlobalGetAtomName (m_aTopic, szFile, MAX_STR) == 0)
|
|
{
|
|
hresult = E_UNEXPECTED;
|
|
goto exitRtn;
|
|
}
|
|
aOriginal = wDupAtom (m_aTopic);
|
|
intrAssert(wIsValidAtom(aOriginal));
|
|
|
|
intrDebugOut((DEB_ITRACE,
|
|
"::DocumentLevelConnect(szFile=%ws)\n",this,szFile));
|
|
if (NOERROR != InitializeIterator (szFile))
|
|
{
|
|
// szFile probably not a network file
|
|
hresult = ResultFromScode (S_FALSE);
|
|
goto errRtn;
|
|
}
|
|
|
|
while (NOERROR == NextEquivalentNetDrive (szFile))
|
|
{
|
|
SetTopic (aTopic = wGlobalAddAtom (szFile));
|
|
if (NOERROR==m_ProxyMgr.Connect (IID_NULL, CLSID_NULL))
|
|
{
|
|
// Inform client of new drive letter
|
|
ChangeTopic (wAtomNameA(aTopic));
|
|
hresult = NOERROR;
|
|
goto errRtn;
|
|
}
|
|
else
|
|
{
|
|
SetTopic ((ATOM)0);
|
|
}
|
|
}
|
|
|
|
// Try with full UNC name
|
|
lstrcpyW (szUNCFile, szOriginalUNCName);
|
|
lstrcatW (szUNCFile, szFile+2); // skip X:
|
|
SetTopic (aTopic = wGlobalAddAtom (szUNCFile));
|
|
if (NOERROR==m_ProxyMgr.Connect (IID_NULL, CLSID_NULL))
|
|
{
|
|
// Inform client of new name
|
|
ChangeTopic (wAtomNameA(aTopic));
|
|
hresult = NOERROR;
|
|
goto errRtn;
|
|
}
|
|
else
|
|
{
|
|
SetTopic ((ATOM)0);
|
|
}
|
|
|
|
// Not running
|
|
hresult = S_FALSE;
|
|
|
|
errRtn:
|
|
if (NOERROR != hresult)
|
|
SetTopic (aOriginal);
|
|
delete szOriginalUNCName;
|
|
szOriginalUNCName = NULL;
|
|
|
|
exitRtn:
|
|
intrDebugOut((DEB_ITRACE,
|
|
"CDdeObject::DocumentLevelConnect(%x) returns %x\n",
|
|
this,hresult));
|
|
|
|
return hresult;
|
|
}
|