You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
683 lines
18 KiB
683 lines
18 KiB
//////////////////////////////////////////////////////////////////////////////
|
|
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1997 - 1999
|
|
|
|
Module Name:
|
|
|
|
MMCUtility.cpp
|
|
|
|
Abstract:
|
|
|
|
Implementation file for functions doing various handy things
|
|
that were getting written over and over again.
|
|
|
|
|
|
Author:
|
|
|
|
Michael A. Maguire 02/05/98
|
|
|
|
Revision History:
|
|
mmaguire 02/05/98 - created
|
|
mmaguire 11/03/98 - moved GetSdo/PutSdo wrappers to sdohelperfuncs.cpp
|
|
|
|
--*/
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// BEGIN INCLUDES
|
|
//
|
|
// standard includes:
|
|
//
|
|
#include "Precompiled.h"
|
|
//
|
|
// where we can find declaration for main class in this file:
|
|
//
|
|
#include "MMCUtility.h"
|
|
#include "cnctdlg.h"
|
|
//
|
|
//
|
|
// where we can find declarations needed in this file:
|
|
//
|
|
|
|
//
|
|
// END INCLUDES
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
/*++
|
|
|
|
HRESULT BringUpPropertySheetForNode(
|
|
CSnapInItem *pSnapInItem
|
|
, IComponentData *pComponentData
|
|
, IComponent *pComponent
|
|
, IConsole *pConsole
|
|
, BOOL bCreateSheetIfOneIsntAlreadyUp = FALSE
|
|
, LPCTSTR lpszSheetTitle = NULL
|
|
, BOOL bPropertyPage = TRUE // TRUE creates property page, FALSE wizard page.
|
|
);
|
|
|
|
|
|
Tries to bring up a property sheet on a given node. If the sheet for the
|
|
node is already up, it will bring that sheet to the foreground.
|
|
|
|
|
|
Parameters:
|
|
|
|
pSnapInItem
|
|
|
|
You must supply a pointer to the node you want the sheet for.
|
|
|
|
|
|
pComponentData, pComponent
|
|
|
|
Either you call with pComponentData != NULL and pComponent == NULL
|
|
or pComponentData == NULL and pComponent != NULL.
|
|
|
|
pConsole
|
|
|
|
You must supply a pointer to an IConsole interface.
|
|
|
|
bCreateSheetIfOneIsntAlreadyUp
|
|
|
|
TRUE - if a sheet isn't already up, will try to create a property sheet
|
|
for you -- in this case you _must_ specify a title for the
|
|
sheet in lpszSheetTitle.
|
|
|
|
FALSE - will try to bring already existing sheet to foreground, but
|
|
will return immediately if there isn't one.
|
|
|
|
bPropertyPage
|
|
|
|
TRUE for property pages. (Note: MMC creates property sheet in new thread.)
|
|
FALSE for wizard pages. (Note: Wizard pages run in same thread.)
|
|
|
|
|
|
Return:
|
|
|
|
S_OK if found property sheet already up.
|
|
S_FALSE if didn't find sheet already up but successfully made new one appear.
|
|
E_... if some error error occurred.
|
|
|
|
|
|
Remarks:
|
|
|
|
For this function's to work, you must have correctly implemented
|
|
IComponentData::CompareObjects and IComponentData::CompareObjects.
|
|
|
|
--*/
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
HRESULT BringUpPropertySheetForNode(
|
|
CSnapInItem *pSnapInItem
|
|
, IComponentData *pComponentData
|
|
, IComponent *pComponent
|
|
, IConsole *pConsole
|
|
, BOOL bCreateSheetIfOneIsntAlreadyUp
|
|
, LPCTSTR lpszSheetTitle
|
|
, BOOL bPropertyPage
|
|
, DWORD dwPropertySheetOptions
|
|
)
|
|
{
|
|
ATLTRACE(_T("# BringUpPropertySheetForNode\n"));
|
|
|
|
|
|
// Check for preconditions:
|
|
_ASSERTE( pSnapInItem != NULL );
|
|
|
|
// We need one or the other to be non-NULL
|
|
_ASSERTE( pComponentData != NULL || pComponent != NULL );
|
|
_ASSERTE( pConsole != NULL );
|
|
|
|
|
|
HRESULT hr;
|
|
|
|
// Query IConsole for the needed interface.
|
|
CComQIPtr<IPropertySheetProvider, &IID_IPropertySheetProvider> spPropertySheetProvider( pConsole );
|
|
_ASSERTE( spPropertySheetProvider != NULL );
|
|
|
|
|
|
CComPtr<IDataObject> spDataObject;
|
|
|
|
hr = pSnapInItem->GetDataObject( &spDataObject, CCT_RESULT );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// This returns S_OK if a property sheet for this object already exists
|
|
// and brings that property sheet to the foreground.
|
|
// It returns S_FALSE if the property sheet wasn't found.
|
|
// If this is coming in through my IComponent object, I pass the pComponent pointer.
|
|
// If this is coming in through my IComponentData object,
|
|
// then pComponent is NULL, which is the appropriate value to pass in for
|
|
// the call to FindPropertySheet when coming in through IComponentData.
|
|
hr = spPropertySheetProvider->FindPropertySheet(
|
|
(MMC_COOKIE) pSnapInItem // cookie
|
|
, pComponent
|
|
, spDataObject
|
|
);
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
|
|
if( S_OK == hr || FALSE == bCreateSheetIfOneIsntAlreadyUp )
|
|
{
|
|
// We found a property sheet already up for this node,
|
|
// or we didn't find one but we weren't asked to create one.
|
|
return hr;
|
|
}
|
|
|
|
|
|
// We didn't find a property sheet already up for this node.
|
|
_ASSERTE( S_FALSE == hr );
|
|
|
|
|
|
hr = spPropertySheetProvider->CreatePropertySheet(
|
|
lpszSheetTitle
|
|
, (BOOLEAN)bPropertyPage /* TRUE == prop page, FALSE == wizard */
|
|
, (MMC_COOKIE) pSnapInItem // cookie
|
|
, spDataObject
|
|
, dwPropertySheetOptions
|
|
);
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
HWND hWndNotification;
|
|
HWND hWndMain;
|
|
|
|
hr = pConsole->GetMainWindow( & hWndMain );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// Try to get the correct window that notifications should be sent to.
|
|
hWndNotification = FindWindowEx( hWndMain, NULL, L"MDIClient", NULL );
|
|
hWndNotification = FindWindowEx( hWndNotification, NULL, L"MMCChildFrm", NULL );
|
|
hWndNotification = FindWindowEx( hWndNotification, NULL, L"MMCView", NULL );
|
|
|
|
if( NULL == hWndNotification )
|
|
{
|
|
// It was a nice try, but it failed, so we should be able to get by by using the main HWND.
|
|
hWndNotification = hWndMain;
|
|
}
|
|
|
|
|
|
|
|
hr = spPropertySheetProvider->AddPrimaryPages(
|
|
(pComponent != NULL ? (LPUNKNOWN) pComponent : (LPUNKNOWN) pComponentData )
|
|
, TRUE
|
|
, hWndNotification
|
|
, (pComponent != NULL ? FALSE : TRUE )
|
|
);
|
|
if( FAILED( hr ) )
|
|
{
|
|
// Release data allocated in CreatePropertySheet
|
|
spPropertySheetProvider->Show( -1, 0);
|
|
return hr;
|
|
}
|
|
|
|
hr = spPropertySheetProvider->AddExtensionPages();
|
|
if( FAILED( hr ) )
|
|
{
|
|
// ISSUE: Should I care if this fails?
|
|
|
|
// Release data allocated in CreatePropertySheet
|
|
spPropertySheetProvider->Show( -1, 0);
|
|
return hr;
|
|
}
|
|
|
|
hr = spPropertySheetProvider->Show( (LONG_PTR) hWndMain, 0);
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ShowErrorDialog(
|
|
HWND hWnd
|
|
, UINT uErrorID
|
|
, BSTR bstrSupplementalErrorString
|
|
, HRESULT hr
|
|
, UINT uTitleID
|
|
, IConsole *pConsole
|
|
, UINT uType
|
|
)
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
/*++
|
|
|
|
Puts up an error dialog with varying degrees of detail
|
|
|
|
Parameters:
|
|
|
|
All parameters are optional -- in the worst case, you can simply call
|
|
|
|
ShowErrorDialog();
|
|
|
|
to put up a very generic error message.
|
|
|
|
|
|
uErrorID
|
|
|
|
The resource ID of the string to be used for the error message.
|
|
Passing in USE_DEFAULT gives causes the default error message to be displayed.
|
|
Passing in USE_SUPPLEMENTAL_ERROR_STRING_ONLY causes no resource string text to be displayed.
|
|
|
|
bstrSupplementalErrorString
|
|
|
|
Pass in a string to print as the error message. Useful if you are
|
|
receiving an error string from some other component you communicate with.
|
|
|
|
hr
|
|
|
|
If there is an HRESULT involved in the error, pass it in here so that
|
|
a suitable error message based on the HRESULT can be put up.
|
|
Pass in S_OK if the HRESULT doesn't matter to the error.
|
|
|
|
|
|
uTitleID
|
|
|
|
The resource ID of the string to be used for the error dialog title.
|
|
Passing in USE_DEFAULT gives causes the default error dialog title to be displayed.
|
|
|
|
pConsole
|
|
|
|
If you are running within the main MMC context, pass in a valid IConsole pointer
|
|
and ShowErrorDialog will use MMC's IConsole::MessageBox rather than the
|
|
standard system MessageBox.
|
|
|
|
hWnd
|
|
|
|
Whatever you pass in here will be passed in as the HWND parameter
|
|
to the MessageBox call.
|
|
This is not used if you pass in an IConsole pointer.
|
|
|
|
uType
|
|
|
|
Whatever you pass in here will be passed in as the HWND parameter
|
|
to the MessageBox call.
|
|
|
|
|
|
Return:
|
|
|
|
The standard int returned from MessageBox.
|
|
|
|
|
|
|
|
--*/
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
{
|
|
ATLTRACE(_T("# ShowErrorDialog\n"));
|
|
|
|
|
|
// Check for preconditions:
|
|
// None.
|
|
|
|
int iReturnValue;
|
|
TCHAR szError[IAS_MAX_STRING*2];
|
|
TCHAR szTitle[IAS_MAX_STRING*2];
|
|
int iLoadStringResult;
|
|
|
|
szError[0] = NULL;
|
|
szTitle[0] = NULL;
|
|
|
|
|
|
HINSTANCE hInstance = _Module.GetResourceInstance();
|
|
|
|
|
|
if( USE_DEFAULT == uTitleID )
|
|
{
|
|
uTitleID = IDS_ERROR__GENERIC_TITLE;
|
|
|
|
}
|
|
|
|
iLoadStringResult = LoadString( hInstance, uTitleID, szTitle, IAS_MAX_STRING );
|
|
_ASSERT( iLoadStringResult > 0 );
|
|
|
|
|
|
|
|
if( USE_SUPPLEMENTAL_ERROR_STRING_ONLY == uErrorID )
|
|
{
|
|
// Special case. We have no text to load from the resources.
|
|
}
|
|
else
|
|
{
|
|
|
|
if( USE_DEFAULT == uErrorID )
|
|
{
|
|
uErrorID = IDS_ERROR__GENERIC;
|
|
}
|
|
|
|
iLoadStringResult = LoadString( hInstance, uErrorID, szError, IAS_MAX_STRING*2 );
|
|
_ASSERT( iLoadStringResult > 0 );
|
|
|
|
if( NULL != bstrSupplementalErrorString )
|
|
{
|
|
// Add some spacing.
|
|
_tcscat( szError, _T(" ") );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( NULL != bstrSupplementalErrorString )
|
|
{
|
|
|
|
// We were passed a string with supplemental error info.
|
|
_tcscat( szError, bstrSupplementalErrorString );
|
|
|
|
}
|
|
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
#if 0 // change to display system wide error info
|
|
// The HRESULT contains some information about the kind of failure.
|
|
|
|
// We may want to change this later to provide more information
|
|
// information based on the error that was returned.
|
|
|
|
// We could have a map which defines relationships between error
|
|
// ID's and the HRESULTS. That way we could provide the appropriate
|
|
// error message for each HRESULT based on the context of which ID
|
|
// was passed in.
|
|
|
|
// For now, just print the error ID.
|
|
|
|
TCHAR szErrorNumber[IAS_MAX_STRING];
|
|
|
|
_stprintf( szErrorNumber, _T(" 0x%x"), hr );
|
|
|
|
// Some spacing.
|
|
_tcscat( szError, _T(" ") );
|
|
|
|
_tcscat( szError, szErrorNumber );
|
|
#endif
|
|
PTSTR ptzSysMsg;
|
|
int cch;
|
|
|
|
cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(PTSTR)&ptzSysMsg, 0, NULL);
|
|
|
|
if (!cch) { //try ads errors
|
|
HMODULE adsMod;
|
|
adsMod = GetModuleHandle (L"odbc32.dll");
|
|
cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE,
|
|
adsMod, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(PTSTR)&ptzSysMsg, 0, NULL);
|
|
}
|
|
|
|
if(cch) // found
|
|
{
|
|
_tcscat( szError, _T(" ") );
|
|
|
|
_tcscat( szError, ptzSysMsg );
|
|
|
|
LocalFree( ptzSysMsg );
|
|
}
|
|
else
|
|
{
|
|
TCHAR szErrorNumber[IAS_MAX_STRING];
|
|
|
|
if(hr == DB_E_NOTABLE) // assume, the RPC connection has problem
|
|
iLoadStringResult = LoadString( hInstance, IDS_ERROR__RESTART_SNAPIN, szErrorNumber, IAS_MAX_STRING );
|
|
else
|
|
{
|
|
_stprintf( szErrorNumber, _T(" 0x%x"), hr );
|
|
|
|
// Some spacing.
|
|
_tcscat( szError, _T(" ") );
|
|
|
|
_tcscat( szError, szErrorNumber );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// Put the error message up in the appropriate manner depending on our context.
|
|
if( pConsole != NULL )
|
|
{
|
|
pConsole->MessageBox( szError, szTitle, uType, &iReturnValue );
|
|
}
|
|
else
|
|
{
|
|
// ISSUE: Do we want to do anything special if hWnd == NULL?
|
|
// Or just pass the NULL right on to MessageBox?
|
|
iReturnValue = ::MessageBox( hWnd, szError, szTitle, uType );
|
|
}
|
|
|
|
return iReturnValue;
|
|
}
|
|
|
|
|
|
|
|
BOOL GetUserAndDomainName( LPTSTR UserName
|
|
, LPDWORD cchUserName
|
|
, LPTSTR DomainName
|
|
, LPDWORD cchDomainName
|
|
)
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
/*++
|
|
|
|
Retrieves current user's username and domain.
|
|
|
|
Stolen from Knowledge Base HOWTO article:
|
|
|
|
HOWTO: Look Up Current User Name and Domain Name
|
|
Rollup: WINPROG
|
|
Database: win32sdk
|
|
Article ID: Q155698
|
|
Last modified: June 16, 1997
|
|
Security: PUBLIC
|
|
|
|
|
|
--*/
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
{
|
|
HANDLE hToken;
|
|
#define MY_BUFSIZE 512 // highly unlikely to exceed 512 bytes
|
|
UCHAR InfoBuffer[ MY_BUFSIZE ];
|
|
DWORD cbInfoBuffer = MY_BUFSIZE;
|
|
SID_NAME_USE snu;
|
|
BOOL bSuccess;
|
|
|
|
if( !OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken) )
|
|
{
|
|
if(GetLastError() == ERROR_NO_TOKEN)
|
|
{
|
|
//
|
|
// attempt to open the process token, since no thread token
|
|
// exists
|
|
//
|
|
if(!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken ))
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// error trying to get thread token
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
bSuccess = GetTokenInformation( hToken, TokenUser, InfoBuffer, cbInfoBuffer, &cbInfoBuffer );
|
|
if(!bSuccess)
|
|
{
|
|
if( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
//
|
|
// alloc buffer and try GetTokenInformation() again
|
|
//
|
|
CloseHandle(hToken);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// error getting token info
|
|
//
|
|
CloseHandle(hToken);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
CloseHandle(hToken);
|
|
|
|
return LookupAccountSid( NULL
|
|
, ((PTOKEN_USER)InfoBuffer)->User.Sid
|
|
, UserName
|
|
, cchUserName
|
|
, DomainName
|
|
, cchDomainName
|
|
, &snu
|
|
);
|
|
}
|
|
|
|
|
|
// Tries to open the specified service with the desired access.
|
|
DWORD TryOpenService(
|
|
const wchar_t* machineName,
|
|
const wchar_t* serviceName,
|
|
DWORD desiredAccess
|
|
) throw ()
|
|
{
|
|
SC_HANDLE manager = OpenSCManager(
|
|
machineName,
|
|
SERVICES_ACTIVE_DATABASE,
|
|
GENERIC_READ
|
|
);
|
|
if (manager == 0)
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
SC_HANDLE service = OpenService(
|
|
manager,
|
|
serviceName,
|
|
desiredAccess
|
|
);
|
|
DWORD result;
|
|
if (service != 0)
|
|
{
|
|
CloseServiceHandle(service);
|
|
result = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
result = GetLastError();
|
|
}
|
|
|
|
CloseServiceHandle(manager);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
HRESULT IfServiceInstalled(
|
|
const wchar_t* machineName,
|
|
const wchar_t* serviceName,
|
|
BOOL* result
|
|
)
|
|
{
|
|
if ((serviceName == 0) || (result == 0))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
*result = FALSE;
|
|
|
|
// We request GENERIC_EXECUTE access because this function is used both to
|
|
// test if the service is installed and to check if we have permission to
|
|
// administer the service.
|
|
DWORD error = TryOpenService(machineName, serviceName, GENERIC_EXECUTE);
|
|
|
|
// If the user doesn't have access, we'll give him a chance to specify
|
|
// alternate credentials.
|
|
while (error == ERROR_ACCESS_DENIED)
|
|
{
|
|
HRESULT hr = ConnectAsAdmin(machineName);
|
|
if (hr == S_FALSE)
|
|
{
|
|
// User chose cancel.
|
|
return E_ACCESSDENIED;
|
|
}
|
|
else if (SUCCEEDED(hr))
|
|
{
|
|
error = TryOpenService(machineName, serviceName, GENERIC_EXECUTE);
|
|
}
|
|
else
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (error == NO_ERROR)
|
|
{
|
|
*result = TRUE;
|
|
}
|
|
else if (error != ERROR_SERVICE_DOES_NOT_EXIST)
|
|
{
|
|
return HRESULT_FROM_WIN32(error);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*!--------------------------------------------------------------------------
|
|
GetModuleFileNameOnly
|
|
-
|
|
Author: WeiJiang
|
|
returns zero on errors
|
|
---------------------------------------------------------------------------*/
|
|
DWORD GetModuleFileNameOnly(HINSTANCE hInst, LPTSTR lpFileName, DWORD nSize )
|
|
{
|
|
CString name;
|
|
TCHAR FullName[MAX_PATH * 2];
|
|
DWORD dwErr = ::GetModuleFileName(hInst, FullName, MAX_PATH * 2);
|
|
|
|
if ((dwErr == 0) || (dwErr == (MAX_PATH * 2)))
|
|
{
|
|
// error or string truncated
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
name = FullName;
|
|
DWORD FirstChar = name.ReverseFind(_T('\\')) + 1;
|
|
|
|
name = name.Mid(FirstChar);
|
|
DWORD len = name.GetLength();
|
|
|
|
if( len < nSize )
|
|
{
|
|
_tcscpy(lpFileName, name);
|
|
}
|
|
else
|
|
{
|
|
len = 0;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
}
|
|
|
|
|