956 lines
20 KiB
C++
956 lines
20 KiB
C++
/*++
|
|
|
|
Copyright (c) 1994-1998 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
menuex.cpp
|
|
|
|
Abstract:
|
|
|
|
Menu extension classes
|
|
|
|
Author:
|
|
|
|
Ronald Meijer (ronaldm)
|
|
|
|
Project:
|
|
|
|
Internet Services Manager
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "inetmgr.h"
|
|
#include "menuex.h"
|
|
#include "constr.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char BASED_CODE THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
//
|
|
// Static member initialization;
|
|
//
|
|
const TCHAR CISMShellExecutable::s_chField = _T(';');
|
|
const TCHAR CISMShellExecutable::s_chEscape = _T('$');
|
|
const TCHAR CISMShellExecutable::s_chService = _T('S');
|
|
const TCHAR CISMShellExecutable::s_chComputer = _T('C');
|
|
|
|
|
|
|
|
/* static */
|
|
HICON
|
|
CISMShellExecutable::GetShellIcon(
|
|
IN LPCTSTR lpszShellExecutable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Extract icon from the given shell executable
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpszShellExecutable : Executable name (or shell document)
|
|
|
|
Return Value:
|
|
|
|
Handle to the icon or NULL
|
|
|
|
--*/
|
|
{
|
|
SHFILEINFO shfi;
|
|
|
|
::SHGetFileInfo(
|
|
lpszShellExecutable,
|
|
0L,
|
|
&shfi,
|
|
sizeof(shfi),
|
|
SHGFI_ICON | SHGFI_SHELLICONSIZE | SHGFI_SMALLICON
|
|
);
|
|
|
|
//
|
|
// Will be NULL if SHGetFileInfo failed
|
|
//
|
|
return shfi.hIcon;
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
HICON
|
|
CISMShellExecutable::ExtractIcon(
|
|
IN LPCTSTR lpszIconSource,
|
|
IN UINT nIconOffset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Extract icon specified by index from the given file
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpszIconSource : Source of the icon file
|
|
UINT nIconOffset : The 0-based icon index within the file
|
|
|
|
Return Value:
|
|
|
|
Handle to the icon or NULL
|
|
|
|
--*/
|
|
{
|
|
HICON hIconSmall = NULL;
|
|
|
|
::ExtractIconEx(lpszIconSource, nIconOffset, NULL, &hIconSmall, 1);
|
|
|
|
return hIconSmall;
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
LPTSTR
|
|
CISMShellExecutable::GetField(
|
|
IN LPTSTR pchLine OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Similar to strtok, this function reads fields from the source string
|
|
separated by semi-colons. When one is found, it is changed to a NULL,
|
|
and the pointer to the current string is returned. Subsequent calls
|
|
to this function may use pchLine as NULL, meaning continue where we
|
|
left off. This function differs from strtok, in that only the first
|
|
separator character is skipped. Having multiple ones in a row, e.g.
|
|
";;;", would return each field as an empty field, and not just skip
|
|
past all of them. Also, quoted fields are returned in their entirety,
|
|
and any separator characters in them are not treated as separators.
|
|
|
|
Arguments:
|
|
|
|
LPTSTR pchLine : Beginning string pointer or NULL
|
|
|
|
Return Value:
|
|
|
|
String pointer to the next field, or NULL if no further fields are
|
|
available.
|
|
|
|
--*/
|
|
{
|
|
static LPTSTR pchEnd = NULL;
|
|
static BOOL fEOL = FALSE;
|
|
LPTSTR pch;
|
|
|
|
//
|
|
// Initialize beginning line pointer
|
|
//
|
|
if (pchLine != NULL)
|
|
{
|
|
//
|
|
// Starting with a new string
|
|
//
|
|
fEOL = FALSE;
|
|
pch = pchLine;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Continue where we left off if there was any
|
|
// thing more to read
|
|
//
|
|
if (fEOL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Must have been called at least once with non-NULL
|
|
// parameter
|
|
//
|
|
ASSERT(pchEnd != NULL);
|
|
|
|
pch = ++pchEnd;
|
|
}
|
|
|
|
pchEnd = pch;
|
|
|
|
//
|
|
// Quotes are handled seperately
|
|
//
|
|
if (*pchEnd == _T('\"') )
|
|
{
|
|
//
|
|
// Skip ahead to closing quote
|
|
//
|
|
++pch;
|
|
do
|
|
{
|
|
++pchEnd;
|
|
}
|
|
while (*pchEnd && *pchEnd != _T('\"') );
|
|
|
|
if (!*pchEnd)
|
|
{
|
|
fEOL = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*(pchEnd++) = _T('\0');
|
|
}
|
|
|
|
return pch;
|
|
}
|
|
|
|
//
|
|
// Else look for the field separator
|
|
// Allowing for null fields
|
|
//
|
|
if (*pchEnd != CISMShellExecutable::s_chField)
|
|
{
|
|
do
|
|
{
|
|
++pchEnd;
|
|
}
|
|
while (*pchEnd && * pchEnd != CISMShellExecutable::s_chField);
|
|
}
|
|
|
|
if (!*pchEnd)
|
|
{
|
|
fEOL = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pchEnd = _T('\0');
|
|
}
|
|
|
|
return pch;
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
DWORD
|
|
CISMShellExecutable::ExpandEscapeCodes(
|
|
OUT CString & strDest,
|
|
IN LPCTSTR lpSrc,
|
|
IN LPCTSTR lpszComputer OPTIONAL,
|
|
IN LPCTSTR lpszService OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Expand the escape codes in the string with computer or service
|
|
strings. See note at constructor below
|
|
|
|
Arguments:
|
|
|
|
CString & strDest : Destination string
|
|
LPCTSTR lpSrc : Original source string
|
|
LPCTSTR lpszComputer : Computer name
|
|
LPCTSTR lpszService : Service name
|
|
|
|
Return Value:
|
|
|
|
Error return code
|
|
|
|
--*/
|
|
{
|
|
DWORD err = ERROR_SUCCESS;
|
|
|
|
try
|
|
{
|
|
strDest.Empty();
|
|
|
|
while(*lpSrc)
|
|
{
|
|
if (*lpSrc == CISMShellExecutable::s_chEscape)
|
|
{
|
|
TCHAR ch = *(++lpSrc);
|
|
CharUpper((LPTSTR)ch);
|
|
switch(ch)
|
|
{
|
|
case CISMShellExecutable::s_chComputer:
|
|
if (lpszComputer)
|
|
{
|
|
strDest += PURE_COMPUTER_NAME(lpszComputer);
|
|
}
|
|
break;
|
|
|
|
case CISMShellExecutable::s_chService:
|
|
if (lpszService)
|
|
{
|
|
strDest += lpszService;
|
|
}
|
|
break;
|
|
|
|
case CISMShellExecutable::s_chEscape:
|
|
strDest += CISMShellExecutable::s_chEscape;
|
|
break;
|
|
|
|
case _T('\0'):
|
|
default:
|
|
//
|
|
// Ignored
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strDest += *lpSrc;
|
|
}
|
|
|
|
++lpSrc;
|
|
}
|
|
}
|
|
catch(CMemoryException * e)
|
|
{
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
e->Delete();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
//
|
|
// Helper macros to parse description line
|
|
//
|
|
#define PARSE_STR_FIELD(src, dest) \
|
|
{ \
|
|
LPTSTR lp = CISMShellExecutable::GetField((LPTSTR)src);\
|
|
if (lp != NULL) \
|
|
{ \
|
|
dest = lp; \
|
|
TRACEEOLID("Field is " << dest); \
|
|
} \
|
|
}
|
|
|
|
#define PARSE_INT_FIELD(src, dest, def) \
|
|
{ \
|
|
LPTSTR lp = CISMShellExecutable::GetField((LPTSTR)src);\
|
|
dest = (lp != NULL ? ::_ttoi(lp) : def); \
|
|
TRACEEOLID("Numeric field is " << dest); \
|
|
}
|
|
|
|
//
|
|
// Helper macro for conditional expansion
|
|
//
|
|
#define SAFE_EXPAND(err, dest, src, server, service) \
|
|
{ \
|
|
err = ExpandEscapeCodes(dest, src, server, service); \
|
|
if (err != ERROR_SUCCESS) \
|
|
{ \
|
|
break; \
|
|
} \
|
|
}
|
|
|
|
|
|
|
|
CISMShellExecutable::CISMShellExecutable(
|
|
IN LPCTSTR lpszRegistryValue,
|
|
IN int nBitmapID,
|
|
IN int nCmd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor. Read the description from the registry and initialize the
|
|
fields.
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpszRegistryValue : Registry value
|
|
int nButton : Toolbar ID
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
Notes:
|
|
|
|
Fields
|
|
=======
|
|
|
|
The registry description consists of fields separated by semi-colons
|
|
The fields are as follows:
|
|
|
|
1) executable name : Executable name or shell document
|
|
2) tool tips text : Text to appear in the toolbar
|
|
3) selection arguments : Command line options if there is a selection
|
|
4) non-selection arguments : Command line options if nothing is selected
|
|
5) working directory : CWD if not set
|
|
6) show in toolbar : If zero, not shown in toolbar, anything else
|
|
will show in toolbar.
|
|
CAVEAT: Empty is translated to mean 'yes'
|
|
7) icon path : Where to get the toolbar icon (if empty,
|
|
the executable name will be used
|
|
8) icon index : If the above is specified, the icon index
|
|
0 otherwise
|
|
|
|
1) Is mandatory, all other fields are optional.
|
|
|
|
Escape Codes
|
|
============
|
|
|
|
Any field may contain escape codes that are filled in at runtime. These
|
|
escape codes are as follows:
|
|
|
|
$S : Replaced with service name (if selected -- skipped otherwise)
|
|
$C : Replaced with computer name (if selected -- skipped otherwise)
|
|
$$ : Replaced with a single $
|
|
|
|
--*/
|
|
: m_strCommand(),
|
|
m_strParms(),
|
|
m_strNoSelectionParms(),
|
|
m_strToolTipsText(),
|
|
m_hIcon(NULL),
|
|
m_pBitmap(NULL),
|
|
m_pmmcButton(NULL),
|
|
m_nBitmapID(nBitmapID),
|
|
m_nCmd(nCmd),
|
|
m_fShowInToolbar(TRUE)
|
|
{
|
|
try
|
|
{
|
|
CString strIconFile;
|
|
UINT nIconOffset;
|
|
|
|
PARSE_STR_FIELD(lpszRegistryValue, m_strCommand);
|
|
PARSE_STR_FIELD(NULL, m_strToolTipsText);
|
|
PARSE_STR_FIELD(NULL, m_strParms);
|
|
PARSE_STR_FIELD(NULL, m_strNoSelectionParms);
|
|
PARSE_STR_FIELD(NULL, m_strDefaultDirectory);
|
|
PARSE_INT_FIELD(NULL, m_fShowInToolbar, TRUE);
|
|
PARSE_STR_FIELD(NULL, strIconFile);
|
|
PARSE_INT_FIELD(NULL, nIconOffset, 0);
|
|
|
|
//
|
|
// If no tooltips text, give it the executable name
|
|
//
|
|
if (m_strToolTipsText.IsEmpty())
|
|
{
|
|
m_strToolTipsText = m_strCommand;
|
|
}
|
|
|
|
if (!m_strCommand.IsEmpty() && m_fShowInToolbar)
|
|
{
|
|
//
|
|
// If no specific icon source file is specified,
|
|
// just use what the shell would use.
|
|
//
|
|
if (strIconFile.IsEmpty())
|
|
{
|
|
m_hIcon = GetShellIcon(m_strCommand);
|
|
}
|
|
else
|
|
{
|
|
m_hIcon = ExtractIcon(strIconFile, nIconOffset);
|
|
}
|
|
|
|
//
|
|
// Provide the ? icon if nothing is proviced in the binary
|
|
//
|
|
if (m_hIcon == NULL)
|
|
{
|
|
m_pBitmap = new CBitmap;
|
|
|
|
if (!m_pBitmap->LoadMappedBitmap(IDB_UNKNOWN))
|
|
{
|
|
TRACEEOLID("Can't load unknown toolbar button bitmap");
|
|
delete m_pBitmap;
|
|
m_pBitmap = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ExtractBitmapFromIcon(m_hIcon, m_pBitmap);
|
|
}
|
|
|
|
//
|
|
// Now build the MMC button structure
|
|
//
|
|
if (HasBitmap())
|
|
{
|
|
m_pmmcButton = (MMCBUTTON *)AllocMem(sizeof(MMCBUTTON));
|
|
if (m_pmmcButton != NULL)
|
|
{
|
|
m_pmmcButton->nBitmap = m_nBitmapID;
|
|
m_pmmcButton->idCommand = m_nCmd;
|
|
m_pmmcButton->fsState = TBSTATE_ENABLED;
|
|
m_pmmcButton->fsType = TBSTYLE_BUTTON;
|
|
m_pmmcButton->lpButtonText = _T(" ");
|
|
m_pmmcButton->lpTooltipText = (LPTSTR)(LPCTSTR)m_strToolTipsText;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch(CMemoryException * e)
|
|
{
|
|
TRACEEOLID("!!!Exception initializing add-on-tool");
|
|
e->ReportError();
|
|
e->Delete();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
CISMShellExecutable::~CISMShellExecutable()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor
|
|
|
|
Arguments:
|
|
|
|
N/A
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
--*/
|
|
{
|
|
if (m_pBitmap)
|
|
{
|
|
//
|
|
// I don't think I own this...
|
|
//
|
|
m_pBitmap->DeleteObject();
|
|
delete m_pBitmap;
|
|
}
|
|
|
|
if (m_pmmcButton)
|
|
{
|
|
FreeMem(m_pmmcButton);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
CISMShellExecutable::ExtractBitmapFromIcon(
|
|
IN HICON hIcon,
|
|
OUT CBitmap *& pBitmap
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Extract bitmap information from icon
|
|
|
|
Arguments:
|
|
|
|
HICON hIcon : Input icon handle
|
|
CBitmap *& pBitmap : Returns bitmap info
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
try
|
|
{
|
|
pBitmap = new CBitmap();
|
|
|
|
//
|
|
// Get bitmap info from icon
|
|
//
|
|
ICONINFO ii;
|
|
ASSERT(hIcon != NULL);
|
|
::GetIconInfo(hIcon, &ii);
|
|
|
|
BITMAP bm;
|
|
|
|
//
|
|
// Determine size of the image
|
|
//
|
|
::GetObject(ii.hbmColor, sizeof(bm), &bm);
|
|
|
|
//
|
|
// Now create a new bitmap by drawing the icon on
|
|
// a button face background colour
|
|
//
|
|
CDC dcMem;
|
|
|
|
dcMem.CreateCompatibleDC(NULL);
|
|
pBitmap->CreateBitmapIndirect(&bm);
|
|
|
|
CBitmap * pOld = dcMem.SelectObject(pBitmap);
|
|
|
|
COLORREF crOld = dcMem.SetBkColor(::GetSysColor(COLOR_BTNFACE));
|
|
CRect rc(0, 0, bm.bmWidth, bm.bmHeight);
|
|
CBrush br(::GetSysColor(COLOR_BTNFACE));
|
|
dcMem.FillRect(&rc, &br);
|
|
|
|
::DrawIconEx(
|
|
dcMem.m_hDC,
|
|
0,
|
|
0,
|
|
hIcon,
|
|
bm.bmWidth,
|
|
bm.bmHeight,
|
|
0,
|
|
NULL,
|
|
DI_NORMAL
|
|
);
|
|
|
|
dcMem.SetBkColor(crOld);
|
|
dcMem.SelectObject(pOld);
|
|
}
|
|
catch(CException * e)
|
|
{
|
|
TRACEEOLID("!!! Exception adding icon based button");
|
|
e->ReportError();
|
|
e->Delete();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
LPCTSTR
|
|
CISMShellExecutable::GetToolTipsText(
|
|
CString & str,
|
|
IN LPCTSTR lpszServer OPTIONAL,
|
|
IN LPCTSTR lpszService OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the tooltips text. Optionally perform escape code
|
|
expansion
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpstrServer : Currently selected server (or NULL)
|
|
LPCTSTR lpstrService : Currently selected service (or NULL)
|
|
|
|
Return Value:
|
|
|
|
Pointer to string
|
|
|
|
--*/
|
|
{
|
|
ExpandEscapeCodes(str, m_strToolTipsText, lpszServer, lpszService);
|
|
|
|
return (LPCTSTR)str;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
CISMShellExecutable::Execute(
|
|
IN LPCTSTR lpszServer OPTIONAL,
|
|
IN LPCTSTR lpszService OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Execute the current module
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpszServer : Currently selected server (or NULL)
|
|
LPCTSTR lpszService : Currently selected service (or NULL)
|
|
|
|
Return Value:
|
|
|
|
Error return code
|
|
|
|
--*/
|
|
{
|
|
DWORD err = ERROR_SUCCESS;
|
|
CString strCommand;
|
|
CString strParms;
|
|
CString strDefaultDirectory;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Expand escape codes as appropriate
|
|
//
|
|
SAFE_EXPAND(err, strCommand, m_strCommand, lpszServer, lpszService);
|
|
SAFE_EXPAND(err, strParms, lpszServer
|
|
? m_strParms : m_strNoSelectionParms, lpszServer, lpszService);
|
|
SAFE_EXPAND(err, strDefaultDirectory, m_strDefaultDirectory,
|
|
lpszServer, lpszService);
|
|
|
|
if (::ShellExecute(
|
|
NULL,
|
|
_T("open"),
|
|
strCommand,
|
|
strParms,
|
|
strDefaultDirectory,
|
|
SW_SHOW
|
|
) <= (HINSTANCE)32)
|
|
{
|
|
err = ::GetLastError();
|
|
}
|
|
}
|
|
while(FALSE);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* OBSOLETE
|
|
|
|
//
|
|
// Add Machine Page Procedure Name
|
|
//
|
|
#define ADDMACHINEPAGE_PROC "ISMAddMachinePages"
|
|
|
|
|
|
|
|
BOOL
|
|
AddISMPage(
|
|
IN HPROPSHEETPAGE hPage,
|
|
IN LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback function to be used for ISM machine property sheet
|
|
extention modules to add pages to the property sheet.
|
|
|
|
Arguments:
|
|
|
|
HPROPSHEETPAGE hPage : Handle to page to be added
|
|
LPARAM lParam : LParam given to the extention module.
|
|
Privately, this is a cast to the property
|
|
sheet structure.
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE for failure. In case of failure, GetLastError
|
|
will reveal the reason.
|
|
|
|
--/
|
|
{
|
|
//
|
|
// The extention module has been given the propsheet
|
|
// cunningly disguised as an LPARAM. If they've messed
|
|
// with the lparam, this will propably crash.
|
|
//
|
|
PROPSHEETHEADER * ppsh = (PROPSHEETHEADER *)lParam;
|
|
|
|
HPROPSHEETPAGE * pOld = ppsh->phpage;
|
|
ppsh->phpage = (HPROPSHEETPAGE *)AllocMem(ppsh->nPages + 1);
|
|
if (ppsh->phpage == NULL)
|
|
{
|
|
//
|
|
// Memory failure
|
|
//
|
|
ppsh->nPages = 0;
|
|
TRACEEOLID("Memory failure building machine property sheet");
|
|
::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Save the old handles
|
|
//
|
|
if (pOld)
|
|
{
|
|
for (UINT i = 0; i < ppsh->nPages; ++i)
|
|
{
|
|
ppsh->phpage[i] = pOld[i];
|
|
}
|
|
|
|
FreeMem(pOld);
|
|
}
|
|
|
|
//
|
|
// Add the new page
|
|
//
|
|
ppsh->phpage[ppsh->nPages++] = hPage;
|
|
|
|
TRACEEOLID("Succesfully built machine property sheet with "
|
|
<< ppsh->nPages << " pages.");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
CISMMachinePageExt::CISMMachinePageExt(
|
|
IN LPCTSTR lpstrDLLName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Constructor for ISM Machine property sheet extender.
|
|
|
|
Arguments:
|
|
|
|
LPCTSTR lpstrDLLName : Name of the DLL.
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
Notes:
|
|
|
|
This will not attempt to load and resolve the DLL
|
|
|
|
--/
|
|
: m_strDLLName(lpstrDLLName),
|
|
m_hDLL(NULL),
|
|
m_lpfn(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
CISMMachinePageExt::~CISMMachinePageExt()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor
|
|
|
|
Arguments:
|
|
|
|
N/A
|
|
|
|
Return Value:
|
|
|
|
N/A
|
|
|
|
Notes:
|
|
|
|
This will unload the DLL if it is still loaded, but this should
|
|
have been done with the UnLoad method beforehand.
|
|
|
|
--/
|
|
{
|
|
//
|
|
// Should have been unloaded by now
|
|
//
|
|
ASSERT(m_hDLL == NULL);
|
|
if (m_hDLL)
|
|
{
|
|
VERIFY(UnLoad());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Prototype
|
|
//
|
|
DWORD
|
|
ISMAddMachinePages(
|
|
IN LPCTSTR lpstrMachineName,
|
|
IN LPFNADDPROPSHEETPAGE lpfnAddPage,
|
|
IN LPARAM lParam,
|
|
IN HINSTANCE hInstance
|
|
);
|
|
|
|
|
|
|
|
DWORD
|
|
CISMMachinePageExt::Load()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Load the DLL, and resolve the exposed interface
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Error return code;
|
|
|
|
--/
|
|
{
|
|
#ifdef _COMSTATIC
|
|
|
|
m_lpfn = &ISMAddMachinePages;
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
#else
|
|
|
|
//
|
|
// Make sure it's not already loaded
|
|
//
|
|
ASSERT(m_hDLL == NULL);
|
|
m_hDLL = ::AfxLoadLibrary(m_strDLLName);
|
|
if (m_hDLL)
|
|
{
|
|
m_lpfn = (LPFNADDMACHINEPAGE)::GetProcAddress(
|
|
m_hDLL, ADDMACHINEPAGE_PROC);
|
|
|
|
if (m_lpfn)
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return ::GetLastError();
|
|
|
|
#endif // _COMSTATIC
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CISMMachinePageExt::UnLoad()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unload the extention DLL
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE for failure
|
|
|
|
--/
|
|
{
|
|
#ifdef _COMSTATIC
|
|
|
|
return TRUE;
|
|
|
|
#else
|
|
|
|
BOOL fResult = FALSE;
|
|
|
|
if (m_hDLL)
|
|
{
|
|
fResult = ::AfxFreeLibrary(m_hDLL);
|
|
m_hDLL = NULL;
|
|
m_lpfn = NULL;
|
|
}
|
|
|
|
return fResult;
|
|
|
|
#endif // _COMSTATIC
|
|
}
|
|
|
|
|
|
*/
|