windows-nt/Source/XPSP1/NT/sdktools/appparse/appparseweb/querydb.cpp
2020-09-26 16:20:57 +08:00

811 lines
21 KiB
C++

// QueryDB.cpp: Database querying methods.
#include "stdafx.h"
#include "AppParseWeb.h"
#include "AppParseWrapper.h"
#include <oledb.h>
#include <comdef.h>
#include <mshtml.h>
#include <assert.h>
// Progress dialog functions
void InitProgressDialog(char* szText, HANDLE hEvent);
void KillProgressDialog();
// Return true if name matches search string, false otherwise.
bool MatchName(char* szString, char* szSearchIn);
// Tree used to represent parse information
class CTreeNode
{
private:
enum {c_Root, c_Project, c_Module, c_Function} m_eType;
int m_nChildren;
CTreeNode** m_ppChildren;
// Relevent info retrieved from DB
union
{
struct
{
char szName[256];
long lPtolemyID;
} m_ProjectInfo;
struct
{
char szName[256];
} m_ModuleInfo;
struct
{
char szName[256];
} m_FunctionInfo;
};
// HTML generation members
// Unique table and div ID's.
static int m_iCurrDiv;
static int m_iCurrTab;
// Amount of space set aside for HTML content.
static int m_iAllocSize;
// Pointer to HTML content.
static char* m_szHTML;
// Pointer to where the more HTML should be inserted.
static char* m_szCurrHTML;
// Pointer a few kilobytes before the end of the HTML buffer, reaching
// here means we should allocate more space.
static char* m_szFencePost;
// True if this node or one of its subtrees contains the function, false otherwise.
bool ContainsFunction(char* szFuncName)
{
if(m_eType == c_Function)
return MatchName(m_FunctionInfo.szName, szFuncName);
for(int i = 0; i < m_nChildren; i++)
{
if(m_ppChildren[i]->ContainsFunction(szFuncName))
return true;
}
return false;
}
// Write all HTML output
void WriteHTML()
{
static int iDepth = 0;
switch(m_eType)
{
case c_Root:
break;
case c_Project:
// Create a new table and Div in the project
m_iCurrTab++;
m_iCurrDiv++;
wsprintf(m_szCurrHTML,
"<table ID = TAB%d border = 1 width = 100%% style=\"float:right\">\n"
"<tr>\n<td width=1%%>\n"
"<input type=\"button\" ID=DIV%dButton value = \"+\" "
"onClick=\"ShowItem(\'DIV%d\')\">"
"</td>\n"
"<td>%s</td><td width=20%%>%d</td>\n</tr>\n"
"</table>\n"
"<DIV ID=DIV%d style=\"display:none;\">\n",
m_iCurrTab, m_iCurrDiv, m_iCurrDiv,
m_ProjectInfo.szName, m_ProjectInfo.lPtolemyID,
m_iCurrDiv);
m_szCurrHTML += strlen(m_szCurrHTML);
break;
case c_Module:
// Create a new table and div in the project
m_iCurrTab++;
m_iCurrDiv++;
wsprintf(m_szCurrHTML,
"<table ID = TAB%d border = 1 width = %d%% style=\"float:right\">\n"
"<tr>\n<td width=1%%>"
"<input type=\"button\" ID=DIV%dButton value = \"+\" "
"onClick=\"ShowItem(\'DIV%d\')\">"
"</td>\n"
"<td>%s</td>\n</tr>\n"
"</table>\n"
"<DIV ID=DIV%d style=\"display:none;\">\n",
m_iCurrTab, 100-iDepth*5, m_iCurrDiv, m_iCurrDiv,
m_ModuleInfo.szName, m_iCurrDiv );
m_szCurrHTML += strlen(m_szCurrHTML);
break;
case c_Function:
// Create a new table in the project
m_iCurrTab++;
wsprintf(m_szCurrHTML,
"<table ID = TAB%d border = 1 width = %d%% style=\"float:right\">\n"
"<tr>"
"<td>%s</td>\n</tr>\n"
"</table>\n",
m_iCurrTab, 100-iDepth*5, m_FunctionInfo.szName);
m_szCurrHTML += strlen(m_szCurrHTML);
break;
default:
assert(0);
break;
}
// Put in all the HTML for the children
if(m_ppChildren)
{
iDepth++;
for(int i = 0; i < m_nChildren; i++)
m_ppChildren[i]->WriteHTML();
iDepth--;
}
switch(m_eType)
{
case c_Function:
case c_Root:
break;
case c_Project:
case c_Module:
wsprintf(m_szCurrHTML, "</DIV>\n");
m_szCurrHTML += strlen(m_szCurrHTML);
break;
}
// Check if we should allocate more
if(m_szCurrHTML > m_szFencePost)
{
m_iAllocSize *= 2;
char* szNewBuffer = new char[m_iAllocSize];
m_szFencePost = &szNewBuffer[m_iAllocSize - 2 * 1024];
strcpy(szNewBuffer, m_szHTML);
m_szCurrHTML = &szNewBuffer[strlen(szNewBuffer)];
delete m_szHTML;
m_szHTML = szNewBuffer;
}
}
public:
CTreeNode()
{
m_eType = c_Root;
m_nChildren = 0;
m_ppChildren = 0;
assert(m_eType < 50);
}
CTreeNode(SProjectRecord pr)
{
m_eType = c_Project;
m_nChildren = 0;
m_ppChildren = 0;
strcpy(m_ProjectInfo.szName, pr.Name);
m_ProjectInfo.lPtolemyID = pr.PtolemyID;
assert(m_eType < 50);
}
CTreeNode(SModuleRecord mr)
{
m_eType = c_Module;
m_nChildren = 0;
m_ppChildren = 0;
strcpy(m_ModuleInfo.szName, mr.Name);
assert(m_eType < 50);
}
CTreeNode(SFunctionRecord fr)
{
m_eType = c_Function;
m_nChildren = 0;
m_ppChildren = 0;
strcpy(m_FunctionInfo.szName, fr.Name);
assert(m_eType < 50);
}
~CTreeNode()
{
RemoveChildren();
}
// Remove tree nodes that contain no nodes matching the search criteria.
// Returns true if node should be removed, false otherwise.
bool Prune(char* szFunc)
{
assert(m_eType < 50);
// Go through each child
for(int i = 0; i < m_nChildren; i++)
{
// Check if needs to be removed
if(m_ppChildren[i]->Prune(szFunc))
{
// Remove this child.
delete m_ppChildren[i];
m_ppChildren[i] = 0;
}
}
// Update the child list
int nChildren = 0;
for(i = 0; i < m_nChildren; i++)
{
if(m_ppChildren[i])
nChildren++;
}
if(nChildren)
{
CTreeNode** pNew = new CTreeNode*[nChildren];
int iCurr = 0;
for(i = 0; i < m_nChildren; i++)
{
if(m_ppChildren[i])
{
pNew[iCurr++] = m_ppChildren[i];
}
}
delete m_ppChildren;
m_ppChildren = pNew;
assert(iCurr == nChildren);
}
else
{
if(m_ppChildren)
{
delete m_ppChildren;
m_ppChildren = 0;
}
}
m_nChildren = nChildren;
// If we contain no children and we're not a function, we should be removed.
if(m_nChildren == 0 && m_eType != c_Function)
return true;
// Return whether we don't contain the function or not.
return !ContainsFunction(szFunc);
}
// Return a string representing the HTML representation of this tree.
char* GetHTML()
{
// Should only be called on root.
assert(m_eType == c_Root);
// Initially reserve space for 64K of HTML.
m_iAllocSize = 64 * 1024;
if(m_szHTML)
delete m_szHTML;
m_szHTML = new char[m_iAllocSize];
m_szHTML[0] = '\0';
m_szCurrHTML = m_szHTML;
m_szFencePost = &m_szHTML[m_iAllocSize - 2 * 1024];
// Fill it with the HTML for this node and all child nodes.
WriteHTML();
char* szRet = m_szHTML;
m_szHTML = 0;
return szRet;
}
// Remove all children from this node.
void RemoveChildren()
{
assert(m_eType < 50);
while(m_nChildren)
{
delete m_ppChildren[m_nChildren-1];
m_ppChildren[m_nChildren-1] = 0;
m_nChildren--;
}
if(m_ppChildren)
delete m_ppChildren;
m_ppChildren = 0;
assert(m_eType < 50);
}
// Insert a new child.
void InsertChild(CTreeNode* pNew)
{
assert(pNew);
assert(pNew->m_eType < 50);
m_nChildren++;
CTreeNode** pNewList = new CTreeNode*[m_nChildren];
if(m_ppChildren)
{
memcpy(pNewList, m_ppChildren, (m_nChildren-1)*sizeof(CTreeNode*));
delete m_ppChildren;
}
m_ppChildren = pNewList;
m_ppChildren[m_nChildren-1] = pNew;
assert(m_eType < 50);
}
};
// Define the static members of CTreeNode
int CTreeNode::m_iCurrDiv = 0;
int CTreeNode::m_iCurrTab = 0;
int CTreeNode::m_iAllocSize = 0;
char* CTreeNode::m_szHTML = 0;
char* CTreeNode::m_szCurrHTML = 0;
char* CTreeNode::m_szFencePost = 0;
// Global tree info.
CTreeNode g_InfoTreeRoot;
// Return true if name matches search string, false otherwise.
bool MatchName(char* szString, char* szSearchIn)
{
if(strcmp(szSearchIn, "*") == 0)
return true;
char* szSearch = szSearchIn;
while(*szSearch != '\0' && *szString != '\0')
{
// If we get a ?, we don't care and move on to the next
// character.
if(*szSearch == '?')
{
szSearch++;
szString++;
continue;
}
// If we have a wildcard, move to next search string and search for substring
if(*szSearch == '*')
{
char* szCurrSearch;
szSearch++;
if(*szSearch == '\0')
return true;
// Don't change starting point.
szCurrSearch = szSearch;
for(;;)
{
// We're done if we hit another wildcard
if(*szCurrSearch == '*' ||
*szCurrSearch == '?')
{
// Update the permanent search position.
szSearch = szCurrSearch;
break;
}
// At end of both strings, return true.
if((*szCurrSearch == '\0') && (*szString == '\0'))
return true;
// We never found it
if(*szString == '\0')
return false;
// If it doesn't match, start over
if(toupper(*szString) != toupper(*szCurrSearch))
{
// If mismatch on first character
// of search string, move to next
// character in function string.
if(szCurrSearch == szSearch)
szString++;
else
szCurrSearch = szSearch;
}
else
{
szString++;
szCurrSearch++;
}
}
}
else
{
if(toupper(*szString) != toupper(*szSearch))
{
return false;
}
szString++;
szSearch++;
}
}
if((*szString == 0) && ((*szSearch == '\0') || (strcmp(szSearch,"*")==0)))
return true;
else
return false;
}
// Add all functions from a module to the tree.
void BuildFunctions(long lParentID, CTreeNode* pParent, _ConnectionPtr pConn)
{
_RecordsetPtr pFunctions = 0;
pFunctions.CreateInstance(__uuidof(Recordset));
char szQuery[1024];
// Open a recordset of all functions that match
wsprintf(szQuery, "SELECT * FROM FUNCTIONS WHERE MODULEID = %d", lParentID);
pFunctions->Open(szQuery, variant_t((IDispatch*)pConn, true), adOpenKeyset,
adLockOptimistic, adCmdText);
// Bind the record set to a local structure.
IADORecordBinding* pRBFunctions = 0;
HRESULT hr = pFunctions->QueryInterface(__uuidof(IADORecordBinding),
reinterpret_cast<void**>(&pRBFunctions));
if(FAILED(hr))
APError("Unable to acquire record binding interface", hr);
SFunctionRecord fr;
hr = pRBFunctions->BindToRecordset(&fr);
if(FAILED(hr))
APError("Unable to bind recordset", hr);
// Go through each record in the set
VARIANT_BOOL fEOF;
pFunctions->get_EndOfFile(&fEOF);
while(!fEOF)
{
// Create a new node.
CTreeNode* pNewNode = new CTreeNode(fr);
pParent->InsertChild(pNewNode);
pFunctions->MoveNext();
pFunctions->get_EndOfFile(&fEOF);
}
pFunctions->Close();
SafeRelease(pRBFunctions);
}
// Add all modules to the tree.
void BuildModules(long lParentID, CTreeNode* pParent, bool fTopLevel,
_ConnectionPtr pConn, HANDLE hEvent)
{
// Check if we should termiante early.
if(WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
return;
_RecordsetPtr pModules = 0;
pModules.CreateInstance(__uuidof(Recordset));
char szQuery[1024];
// Get recordset that matches
if(fTopLevel)
wsprintf(szQuery, "SELECT * FROM MODULES WHERE PTOLEMYID = %d", lParentID);
else
wsprintf(szQuery, "SELECT * FROM MODULES WHERE PARENTID = %d", lParentID);
pModules->Open(szQuery, variant_t((IDispatch*)pConn, true), adOpenKeyset,
adLockOptimistic, adCmdText);
IADORecordBinding* pRBModules = 0;
HRESULT hr = pModules->QueryInterface(__uuidof(IADORecordBinding),
reinterpret_cast<void**>(&pRBModules));
if(FAILED(hr))
APError("Unable to acquire record binding interface", hr);
SModuleRecord mr;
hr = pRBModules->BindToRecordset(&mr);
if(FAILED(hr))
APError("Unable to bind recordset", hr);
// Go through each record
VARIANT_BOOL fEOF;
pModules->get_EndOfFile(&fEOF);
while(!fEOF)
{
// Insert into tree
CTreeNode* pNewNode = new CTreeNode(mr);
pParent->InsertChild(pNewNode);
// Build all child modules
BuildModules(mr.ModuleID, pNewNode, false, pConn, hEvent);
// Build all functions
BuildFunctions(mr.ModuleID, pNewNode, pConn);
pModules->MoveNext();
pModules->get_EndOfFile(&fEOF);
}
pModules->Close();
SafeRelease(pRBModules);
}
// Add a project to the tree
void BuildProjects(long PtolemyID, char* szFunc, _ConnectionPtr pConn, HANDLE hEvent)
{
assert(PtolemyID > 0);
// Check if we should terminate early
if(WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
return;
_RecordsetPtr pProjects = 0;
pProjects.CreateInstance(__uuidof(Recordset));
char szQuery[1024];
// Get a recordset that matches
wsprintf(szQuery, "SELECT * FROM PROJECTS WHERE PTOLEMYID = %d", PtolemyID);
pProjects->Open(szQuery, variant_t((IDispatch*)pConn, true),adOpenKeyset,
adLockOptimistic, adCmdText);
IADORecordBinding* pRBProjects = 0;
HRESULT hr = pProjects->QueryInterface(__uuidof(IADORecordBinding),
reinterpret_cast<void**>(&pRBProjects));
if(FAILED(hr))
APError("Unable to acquire record binding interface", hr);
SProjectRecord pr;
hr = pRBProjects->BindToRecordset(&pr);
if(FAILED(hr))
APError("Unable to bind recordset", hr);
VARIANT_BOOL fEOF;
pProjects->get_EndOfFile(&fEOF);
while(!fEOF)
{
// Insert the node at the root.
CTreeNode* pNewNode = new CTreeNode(pr);
g_InfoTreeRoot.InsertChild(pNewNode);
// Get all child modules
BuildModules(pr.PtolemyID, pNewNode, true, pConn, hEvent);
// Save memory by trimming tree now.
pNewNode->Prune(szFunc);
pProjects->MoveNext();
pProjects->get_EndOfFile(&fEOF);
}
pProjects->Close();
SafeRelease(pRBProjects);
}
long GetModulePtolemy(long lModuleID, _ConnectionPtr pConn)
{
_RecordsetPtr pModules = 0;
pModules.CreateInstance(__uuidof(Recordset));
char szQuery[1024];
// Get a single record recordset containing the module.
wsprintf(szQuery, "SELECT * FROM MODULES WHERE MODULEID = %d", lModuleID);
pModules->Open(szQuery, variant_t((IDispatch*)pConn, true), adOpenKeyset,
adLockOptimistic, adCmdText);
IADORecordBinding* pRBModules = 0;
HRESULT hr = pModules->QueryInterface(__uuidof(IADORecordBinding),
reinterpret_cast<void**>(&pRBModules));
if(FAILED(hr))
APError("Unable to acquire record binding interface", hr);
SModuleRecord mr;
hr = pRBModules->BindToRecordset(&mr);
if(FAILED(hr))
APError("Unable to bind recordset", hr);
// Either return ptolemy ID, if valid, otherwise call on parent module.
long lParent = mr.ParentID;
if(mr.ParentIDStatus != adFldNull)
{
pModules->Close();
SafeRelease(pRBModules);
return GetModulePtolemy(lParent, pConn);
}
else
{
pModules->Close();
SafeRelease(pRBModules);
return lParent;
}
}
long GetFuncPtolemy(SFunctionRecord fr, _ConnectionPtr pConn)
{
return GetModulePtolemy(fr.ModuleID, pConn);
}
// Build a list projects that contain a function that matches szFunc.
void BuildProjectsFromFunction(char* szFunc, _ConnectionPtr pConn, HANDLE hEvent)
{
// Check if we should terminate early.
if(WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
return;
_RecordsetPtr pFunctions = 0;
pFunctions.CreateInstance(__uuidof(Recordset));
char* szQuery = "SELECT * FROM FUNCTIONS";
pFunctions->Open(szQuery, variant_t((IDispatch*)pConn, true),adOpenKeyset,
adLockOptimistic, adCmdText);
IADORecordBinding* pRBFunctions = 0;
HRESULT hr = pFunctions->QueryInterface(__uuidof(IADORecordBinding),
reinterpret_cast<void**>(&pRBFunctions));
if(FAILED(hr))
APError("Unable to acquire record binding interface", hr);
SFunctionRecord fr;
hr = pRBFunctions->BindToRecordset(&fr);
if(FAILED(hr))
APError("Unable to bind recordset", hr);
VARIANT vtBookMark;
hr = pFunctions->get_Bookmark(&vtBookMark);
if(FAILED(hr))
APError("Unable to get recordset bookmark", hr);
// Do a search for the function
char szFind[512];
int FunctionList[1024] = {0};
wsprintf(szFind, "Name like \'%s\'", szFunc);
pFunctions->Find(szFind, 0, adSearchForward, vtBookMark);
while(!pFunctions->EndOfFile)
{
// Get which module imports this function
long lPtolemy = GetFuncPtolemy(fr, pConn);
assert(lPtolemy > 0);
// Make sure we haven't already touched this module.
bool fInUse = false;
for(int i = 0; i < 1024; i++)
{
if(FunctionList[i] == 0)
{
FunctionList[i] = lPtolemy;
break;
}
else if(FunctionList[i] == lPtolemy)
{
fInUse = true;
}
}
if(!fInUse)
BuildProjects(lPtolemy, szFunc, pConn, hEvent);
hr = pFunctions->get_Bookmark(&vtBookMark);
if(FAILED(hr))
APError("Unable to acquire recordset bookmark", hr);
pFunctions->Find(szFind, 1, adSearchForward, vtBookMark);
}
SafeRelease(pRBFunctions);
pFunctions->Close();
}
STDMETHODIMP CAppParse::QueryDB(long PtolemyID, BSTR bstrFunction)
{
assert(m_hEvent);
try
{
// Start cancelation dialog
ResetEvent(m_hEvent);
InitProgressDialog("Querying database . . .", m_hEvent);
bstr_t bszFunctionSearch = bstrFunction;
char* szFunctionSearch = static_cast<char*>(bszFunctionSearch);
HRESULT hr;
_ConnectionPtr pConn = 0;
pConn.CreateInstance(__uuidof(Connection));
// Connect to the DB
pConn->Open(m_szConnect, "","", adConnectUnspecified);
// Build projects
if(PtolemyID > 0)
BuildProjects(PtolemyID, szFunctionSearch, pConn, m_hEvent);
else
BuildProjectsFromFunction(szFunctionSearch, pConn, m_hEvent);
pConn->Close();
// Check if results should be shown.
if(WaitForSingleObject(m_hEvent, 0) == WAIT_OBJECT_0)
{
g_InfoTreeRoot.RemoveChildren();
KillProgressDialog();
return S_OK;
}
// Trim the tree down.
g_InfoTreeRoot.Prune(szFunctionSearch);
// Get our container document.
CComPtr<IOleContainer> pContainer = 0;
m_spClientSite->GetContainer(&pContainer);
CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> pDoc(pContainer);
if(!pDoc)
APError("Unable to acquire container HTML document", E_FAIL);
CComPtr<IHTMLElementCollection> pElements;
pDoc->get_all(&pElements);
CComPtr<IDispatch> pDispatch = 0;
// Get the element that will contain all HTML output (the "Results" DIV)
hr = pElements->item(variant_t("Results"), variant_t(0L), &pDispatch);
if(FAILED(hr) || !pDispatch)
return E_FAIL;
CComQIPtr<IHTMLElement, &IID_IHTMLElement> pDivElem(pDispatch);
// Get HTML representation of tree.
char* szHTML = g_InfoTreeRoot.GetHTML();
// Convert to wide characters
OLECHAR* oszInnerHTML = new OLECHAR[strlen(szHTML) + 1];
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szHTML,
-1, oszInnerHTML,
(strlen(szHTML)+1)*sizeof(OLECHAR));
delete szHTML;
// Convert to a BSTR
BSTR bszInnerHTML = SysAllocString(oszInnerHTML);
delete oszInnerHTML;
// Write the HTML into the document.
hr = pDivElem->put_innerHTML(bszInnerHTML);
if(FAILED(hr))
APError("Unable to write HTML to container document", hr);
SysFreeString(bszInnerHTML);
}
catch(_com_error& e)
{
::MessageBox(0, (LPCSTR)e.ErrorMessage(), "COM Error", MB_OK);
}
g_InfoTreeRoot.RemoveChildren();
KillProgressDialog();
return S_OK;
}