windows-nt/Source/XPSP1/NT/admin/netui/profext/profext.cxx
2020-09-26 16:20:57 +08:00

725 lines
21 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// includes
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#define INCL_WINDOWS
#define INCL_NETERRORS
#define INCL_NETLIB
#include "lmui.hxx"
#define INCL_BLT_MISC
#include "blt.hxx" // AUTO_CURSOR
#include "dbgstr.hxx"
#include "strnumer.hxx"
#include "uiassert.hxx"
#include "lmobj.hxx"
#include "lmoenum.hxx" // SERVICE_ENUM
#include "lmoesvc.hxx" // SERVICE_ENUM
#include "regkey.hxx" // REG_KEY
#include "lmobjrc.h" // IDS_PROFEXT_*
extern "C"
{
#include <prsht.h>
#include "profext.h"
#include <cfgmgr32.h> // CONFIGRET
#include <regstr.h> // CSCONFIGFLAG_DISABLED
// we have to do this for a clean compile
#undef DBGOUT
#include "sysdm.h" // IDH_HWPROFILE
#include <regstr.h> // REGSTR_PATH_SERVICES
#include <ntddndis.h> // NdisMedium* constants
}
//
// global data
//
HINSTANCE hInst = NULL; // Module handle
static int HwProfileHelpIds[] = {
IDC_DISABLE, IDH_HWPROFILE+IDC_DISABLE,
IDC_ENABLE, IDH_HWPROFILE+IDC_ENABLE,
IDC_NONET, IDH_HWPROFILE+IDC_NONET,
IDC_ERROR, IDH_HWPROFILE+IDC_ERROR,
0, 0
};
#define SZ_NDIS SZ("NDIS")
#define TCH_REGSEPERATOR TCH('\\')
#define SZ_REGSEPERATOR SZ("\\")
#define SZ_REGPARAMETERS SZ("\\Parameters")
#define SZ_REGLINKAGE SZ("\\Linkage")
#define SZ_REGMEDIATYPE SZ("MediaType")
#define SZ_REGBIND SZ("Bind")
APIERR MapCfgMgr32Error( CONFIGRET dwCfgMgr32Error )
{
APIERR err = IDS_CFGMGR32_BASE + dwCfgMgr32Error;
switch (dwCfgMgr32Error)
{
case CR_SUCCESS :
// CR_NEED_RESTART is considered to be Success
case CR_NEED_RESTART :
err = NO_ERROR;
break;
// Just use the CFGMGR out-of-memory error
//
// case CR_OUT_OF_MEMORY :
// err = ERROR_NOT_ENOUGH_MEMORY;
// break;
//
// These are supposedly for Windows 95 only!
//
case CR_INVALID_ARBITRATOR :
case CR_INVALID_NODELIST :
case CR_DLVXD_NOT_FOUND :
case CR_NOT_SYSTEM_VM :
case CR_NO_ARBITRATOR :
case CR_DEVLOADER_NOT_READY :
ASSERT( FALSE );
// fall through
default :
break;
}
return err;
}
VOID
HandleError( HWND hDlg, APIERR err, HWND hwndEdit = NULL )
{
NLS_STR nlsError, nlsTitle;
if ( nlsError.QueryError() != NERR_Success
|| nlsTitle.QueryError() != NERR_Success
|| nlsError.Load( err ) != NERR_Success
|| nlsTitle.Load( IDS_PROFEXT_ERROR ) != NERR_Success
)
{
TRACEEOL( "PROFEXT.DLL: HandleError failure" );
ASSERT( FALSE );
return;
}
if (hwndEdit != NULL)
{
::SetWindowText( hwndEdit, nlsError.QueryPch() );
::ShowWindow( hwndEdit, SW_SHOW );
}
if (err != IDS_PROFEXT_NOADAPTERS)
{
REQUIRE( IDOK == ::MessageBox( hDlg,
nlsError.QueryPch(),
nlsTitle.QueryPch(),
MB_ICONSTOP | MB_OK ) );
}
}
APIERR
IsDisabled( ULONG ulProfileID, DEVINSTID devinst, BOOL* pfIsDisabled )
{
TRACEEOL( "IsDisabled( " << ulProfileID << ", \"" << devinst << "\") " );
ASSERT( pfIsDisabled != NULL );
APIERR err = NERR_Success;
ULONG ulValue = 0;
CONFIGRET configret = ::CM_Get_HW_Prof_Flags(
devinst,
ulProfileID,
&ulValue,
0x0 );
err = MapCfgMgr32Error( configret );
if (err != NERR_Success)
{
TRACEEOL( "\terror " << configret );
return err;
}
*pfIsDisabled = !!(ulValue & CSCONFIGFLAG_DISABLED);
TRACEEOL( (CHAR*)((*pfIsDisabled)?"\tis disabled":"\tis enabled") );
return err;
}
APIERR
Disable( ULONG ulProfileID, DEVINSTID devinst, BOOL fDisable )
{
APIERR err = NERR_Success;
ULONG ulValue = 0;
CONFIGRET configret = ::CM_Get_HW_Prof_Flags(
devinst,
ulProfileID,
&ulValue,
0x0 );
err = MapCfgMgr32Error( configret );
if (err != NERR_Success)
{
TRACEEOL( "\tEnable: CM_Get_HW_Prof_Flags error " << configret );
return err;
}
if (fDisable)
{
if ( ulValue & CSCONFIGFLAG_DISABLED )
{
TRACEEOL( "Disable: \"" << devinst << "\" already disabled" );
return err;
}
ulValue |= CSCONFIGFLAG_DISABLED;
} else {
if ( !(ulValue & CSCONFIGFLAG_DISABLED) )
{
TRACEEOL( "Disable: \"" << devinst << "\" already enabled" );
return err;
}
ulValue &= ~CSCONFIGFLAG_DISABLED;
}
configret = ::CM_Set_HW_Prof_Flags(
devinst,
ulProfileID,
ulValue,
0x0 );
err = MapCfgMgr32Error( configret );
if (err != NERR_Success)
{
TRACEEOL( "\tDisable: CM_Set_HW_Prof_Flags error " << configret );
} else {
TRACEEOL( "\tDisable: \"" << devinst
<< (CHAR*)((fDisable)?"\" now disabled":"\" now enabled") );
}
return err;
}
APIERR BoundServiceIsWanMediaType( NLS_STR& nlsBoundService,
BOOL* pfWANMediaType,
REG_KEY& regkeyHKLM )
{
const TCHAR * pchBoundService = nlsBoundService.QueryPch();
ISTR istrBoundService( nlsBoundService );
if ( nlsBoundService.strrchr( &istrBoundService, TCH_REGSEPERATOR ) )
{
++istrBoundService;
pchBoundService = nlsBoundService.QueryPch( istrBoundService );
}
// open HKLM\CurrentControlSet\Services\<bound service>\Parameters key
NLS_STR nlsRegPath( REGSTR_PATH_SERVICES );
nlsRegPath += SZ_REGSEPERATOR;
nlsRegPath += pchBoundService;
nlsRegPath += SZ_REGPARAMETERS;
APIERR err = nlsRegPath.QueryError();
if ( err != NERR_Success )
{
DBGEOL( "BoundServiceIsWanMediaType: string error " << err );
return err;
}
TRACEEOL( "BoundServiceIsWanMediaType: key \"" << nlsRegPath << "\"" );
REG_KEY regkeyParameters(regkeyHKLM, nlsRegPath, KEY_READ);
if ( (err = regkeyParameters.QueryError()) != NERR_Success )
{
TRACEEOL( "BoundServiceIsWanMediaType: key \"" << nlsRegPath <<
"\" error " << err );
return err;
}
DWORD dwMediaType = 0;
if ( (err = regkeyParameters.QueryValue( SZ_REGMEDIATYPE, &dwMediaType))
!= NERR_Success )
{
TRACEEOL( "BoundServiceIsWanMediaType: mediatype error " << err );
return err;
}
TRACEEOL( "BoundServiceIsWanMediaType: media type " << dwMediaType );
switch (dwMediaType-1)
{
case NdisMedium802_3:
case NdisMedium802_5:
case NdisMediumFddi:
case NdisMediumLocalTalk:
case NdisMediumDix:
case NdisMediumArcnetRaw:
case NdisMediumArcnet878_2:
case NdisMediumAtm:
*pfWANMediaType = FALSE;
break;
case NdisMediumWan:
case NdisMediumWirelessWan:
case NdisMediumIrda:
*pfWANMediaType = TRUE;
break;
default:
DBGEOL( "BoundServiceIsWanMediaType: invalid media type "
<< dwMediaType );
*pfWANMediaType = TRUE;
break;
}
return NERR_Success;
}
APIERR IsWanMediaType( const TCHAR * pszServiceName, BOOL* pfWANMediaType)
{
TRACEEOL( "IsWanMediaType: checking \"" << pszServiceName << "\"" );
// open HKLM key
REG_KEY regkeyHKLM(HKEY_LOCAL_MACHINE, NULL, KEY_READ);
APIERR err = regkeyHKLM.QueryError();
if ( err != NERR_Success )
{
DBGEOL( "IsWanMediaType: HKLM error " << err );
return err;
}
// open HKLM\CurrentControlSet\Services\<service>\Linkage key
NLS_STR nlsRegPath( REGSTR_PATH_SERVICES );
nlsRegPath += SZ_REGSEPERATOR;
nlsRegPath += pszServiceName;
nlsRegPath += SZ_REGLINKAGE;
if ( (err = nlsRegPath.QueryError()) != NERR_Success )
{
DBGEOL( "IsWanMediaType: string error " << err );
return err;
}
TRACEEOL( "IsWanMediaType: key \"" << nlsRegPath << "\"" );
REG_KEY regkeyLinkage(regkeyHKLM, nlsRegPath, KEY_READ);
if ( (err = regkeyLinkage.QueryError()) != NERR_Success )
{
TRACEEOL( "IsWanMediaType: key \"" << nlsRegPath <<
"\" error " << err );
return err;
}
// get Bind parameter and extract service name(s)
STRLIST* pstrlistBind = NULL;
if ( (err = regkeyLinkage.QueryValue( SZ_REGBIND, &pstrlistBind ))
!= NERR_Success
|| (err = ((pstrlistBind != NULL)
? NERR_Success : ERROR_NOT_ENOUGH_MEMORY)) != NERR_Success
)
{
TRACEEOL( "IsWanMediaType: bind value error " << err );
return err;
}
ITER_STRLIST iterslBind( *pstrlistBind );
NLS_STR* pnlsBind = NULL;
*pfWANMediaType = FALSE;
while ( (pnlsBind = iterslBind.Next()) != NULL )
{
err = BoundServiceIsWanMediaType( *pnlsBind,
pfWANMediaType,
regkeyHKLM );
if (err != NERR_Success || *pfWANMediaType )
break;
}
delete pstrlistBind; // CODEWORK I have seen problems with this line
return err;
}
/*
* if fSet is FALSE, set *pfNoNetworkProfile to TRUE if no network
* device is enabled, set to FALSE otherwise
*
* if fSet is TRUE and *pfNoNetworkProfile is FALSE,
* enable all network devices
*
* if fSet is TRUE and *pfNoNetworkProfile is TRUE,
* disable all network devices
*/
APIERR
DoNetworkProfile( ULONG ulProfileID, BOOL fSet, BOOL* pfNoNetworkProfile )
{
AUTO_CURSOR autocur;
ASSERT( pfNoNetworkProfile != NULL );
APIERR err = NERR_Success;
if (!fSet)
*pfNoNetworkProfile=TRUE;
do // false loop
{
TRACEEOL( "DoNetworkProfile: enumerating NDIS services");
SERVICE_ENUM svcenum( NULL, TRUE, SERVICE_KERNEL_DRIVER, SZ_NDIS );
if ( (err = svcenum.QueryError()) != NERR_Success
|| (err = svcenum.GetInfo()) != NERR_Success
)
{
TRACEEOL( "DoNetworkProfile: error " << err << " opening svcenum" );
break;
}
BOOL fFoundAdapter = FALSE;
SERVICE_ENUM_ITER svciter( svcenum );
const SERVICE_ENUM_OBJ* psvc;
while( ( psvc = svciter() ) != NULL )
{
const TCHAR* pszServiceName = psvc->QueryServiceName();
if ( 0 == ::stricmpf( pszServiceName, SZ_NDIS ) )
continue;
BOOL fWANMediaType = FALSE;
if ( (err = IsWanMediaType(pszServiceName, &fWANMediaType))
!= NERR_Success)
{
//
// All errors are ignored, and we assume WAN MediaType
//
TRACEEOL( "DoNetworkProfile: ignoring error " << err <<
" checking mediatype of \"" << pszServiceName <<
"\"" );
err = NERR_Success;
continue;
} else if (fWANMediaType) {
TRACEEOL( "DoNetworkProfile: skipping service \"" <<
pszServiceName << "\" due to mediatype" );
continue;
}
ULONG cch = 0;
CONFIGRET configret = ::CM_Get_Device_ID_List_Size(
&cch,
pszServiceName,
CM_GETIDLIST_FILTER_SERVICE );
if ( (err = MapCfgMgr32Error( configret )) != NERR_Success )
{
TRACEEOL("DoNetworkProfile: Get_Device_ID_List_Size returned " << configret );
if (configret == CR_NO_SUCH_VALUE)
{
ASSERT( FALSE );
continue;
}
break;
}
BUFFER bufDeviceIDs( cch*sizeof(TCHAR) );
if ( (err = bufDeviceIDs.QueryError()) != NERR_Success )
break;
do {
configret = ::CM_Get_Device_ID_List(
pszServiceName,
(TCHAR *)(bufDeviceIDs.QueryPtr()),
bufDeviceIDs.QuerySize() / sizeof(TCHAR),
CM_GETIDLIST_FILTER_SERVICE );
if (configret == CR_BUFFER_SMALL)
{
cch *= 2;
if ( (err = bufDeviceIDs.Resize(cch*sizeof(TCHAR))) != NERR_Success )
break;
} else if (configret == CR_NO_SUCH_VALUE)
{
ASSERT( FALSE );
continue;
}
} while (configret == CR_BUFFER_SMALL);
const TCHAR * pchDeviceID = (const TCHAR *)bufDeviceIDs.QueryPtr();
while ( *pchDeviceID != TCH('\0') )
{
fFoundAdapter = TRUE;
DEVINSTID devinst = (DEVINSTID)pchDeviceID;
if (fSet) {
if ( (err = Disable( ulProfileID,
devinst,
*pfNoNetworkProfile)) != NERR_Success)
break;
} else {
BOOL fIsDisabled = FALSE;
if ( (err = IsDisabled( ulProfileID,
devinst,
&fIsDisabled)) != NERR_Success)
break;
if (!fIsDisabled)
{
*pfNoNetworkProfile = FALSE;
break;
}
}
pchDeviceID += ::strlenf(pchDeviceID) + 1;
}
}
TRACEEOL( "DoNetworkProfile: enumerated NDIS services");
if ( !fFoundAdapter && err == NERR_Success )
{
TRACEEOL( "DoNetworkProfile: no adapters found" );
err = IDS_PROFEXT_NOADAPTERS;
}
} while (FALSE); // false loop
return err;
}
BOOL
DllMain(
PVOID hModule,
ULONG Reason,
PCONTEXT pContext
)
/*++
Routine Description:
This is the standard DLL entrypoint routine, called whenever a process
or thread attaches or detaches.
Arguments:
hModule - PVOID parameter that specifies the handle of the DLL
Reason - ULONG parameter that specifies the reason this entrypoint
was called (either PROCESS_ATTACH, PROCESS_DETACH,
THREAD_ATTACH, or THREAD_DETACH).
pContext - ???
Return value:
Returns true if initialization compeleted successfully, false is not.
--*/
{
hInst = (HINSTANCE)hModule;
DBG_UNREFERENCED_PARAMETER(pContext);
switch(Reason) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hInst);
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
} // DllMain
BOOL CALLBACK
ExtensionPropSheetPageProc(
LPVOID lpv,
LPFNADDPROPSHEETPAGE lpfnAddPropSheetPageProc,
LPARAM lParam
)
/*++
Routine Description:
Arguments:
lpv Pointer to an application-defined value that
describes an item for which a property sheet
page is to be created. This parameter can be NULL.
lpfnAddPropSheetPageProc Pointer to the AddPropSheetPageProc function.
The extension dynamic-link library (DLL) calls
this function to add a page to the property sheet.
lParam Application-defined 32-bit value.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE.
--*/
{
PROPSHEETPAGE PropPage;
HPROPSHEETPAGE hPropPage;
PropPage.dwSize = sizeof(PROPSHEETPAGE);
PropPage.dwFlags = PSP_DEFAULT;
PropPage.hInstance = hInst;
PropPage.pszTemplate = MAKEINTRESOURCE(DLG_HWP_EXT);
PropPage.pszIcon = NULL;
PropPage.pszTitle = NULL;
PropPage.pfnDlgProc = NetworkPropDlg;
PropPage.lParam = lParam;
PropPage.pfnCallback = NULL;
hPropPage = CreatePropertySheetPage(&PropPage);
if (hPropPage == NULL) {
return FALSE;
}
(lpfnAddPropSheetPageProc)(hPropPage, 0);
return TRUE;
} // ExtensionPropSheetPageProc
typedef struct {
LONG ulProfile;
BOOL fNoNet;
BOOL fError;
} PROFEXT_INTERNAL;
BOOL
APIENTRY
NetworkPropDlg(
HWND hDlg,
UINT uMessage,
WPARAM wParam,
LPARAM lParam
)
{
ULONG ulProfile = 0;
APIERR err = NERR_Success;
switch (uMessage)
{
case WM_INITDIALOG:
{
PROFEXT_INTERNAL* pprofext = new PROFEXT_INTERNAL;
// CODEWORK I should free this at some point
ASSERT( pprofext != NULL );
::memsetf( pprofext, '\0', sizeof(PROFEXT_INTERNAL) );
::SetWindowLong(hDlg, DWL_USER, (LONG)pprofext);
//
// on WM_INITDIALOG call, property sheet's lParam field
// contains the profile ID, save it in window long word for
// use with other messages later
//
ulProfile = (ULONG)((LPPROPSHEETPAGE)lParam)->lParam;
ASSERT( (LONG)ulProfile >= 0 );
pprofext->ulProfile = ulProfile;
BOOL fNoNetworkProfile = FALSE;
err = DoNetworkProfile( ulProfile,
FALSE,
&fNoNetworkProfile );
if (err != NERR_Success) {
HWND hwndEdit = ::GetDlgItem(hDlg, IDC_ERROR);
ASSERT( hwndEdit != NULL );
HandleError( hDlg, err, hwndEdit );
pprofext->fError = TRUE;
} else {
pprofext->fNoNet = (fNoNetworkProfile)?1:0;
}
//
// Call CM_Get_Hardware_Profile_Info to get info about
// the specified profile (ulProfile).
//
return FALSE;
}
case WM_NOTIFY:
if (!lParam) {
break;
}
switch (((NMHDR *)lParam)->code) {
case PSN_SETACTIVE:
{
PROFEXT_INTERNAL* pprofext =
(PROFEXT_INTERNAL*)::GetWindowLong(hDlg, DWL_USER);
ASSERT( pprofext != NULL );
HWND hwndCheckbox = ::GetDlgItem(hDlg, IDC_NONET);
HWND hwndEnableText = ::GetDlgItem(hDlg, IDC_ENABLE);
HWND hwndDisableText = ::GetDlgItem(hDlg, IDC_DISABLE);
ASSERT( hwndCheckbox
&& hwndEnableText && hwndDisableText );
::EnableWindow(
hwndEnableText,
pprofext->fNoNet && !(pprofext->fError) );
::EnableWindow(
hwndDisableText,
!(pprofext->fNoNet) && !(pprofext->fError) );
if (pprofext->fError)
{
::EnableWindow( hwndCheckbox, FALSE );
} else {
::SendMessage( hwndCheckbox, BM_SETCHECK,
(pprofext->fNoNet)?1:0, 0 );
}
}
break;
case PSN_KILLACTIVE:
{
PROFEXT_INTERNAL* pprofext =
(PROFEXT_INTERNAL*)::GetWindowLong(hDlg, DWL_USER);
ASSERT( pprofext != NULL );
//
// validate selections, this page is active until I return FALSE
// in DWL_MSGRESULT
//
if (pprofext->fError)
break; // error or no adapter as initial state
TRACEEOL( "NetworkPropDlg: pprofext->fNoNet is "
<< pprofext->fNoNet );
HWND hwnd = ::GetDlgItem(hDlg, IDC_NONET);
ASSERT( hwnd != NULL );
BOOL fCurrentNoNet = (::SendMessage(hwnd, BM_GETCHECK, 0, 0) != 0)
? TRUE : FALSE;
TRACEEOL( "NetworkPropDlg: fCurrentNoNet is " << fCurrentNoNet );
if (pprofext->fNoNet != fCurrentNoNet)
{
err = DoNetworkProfile(
pprofext->ulProfile, TRUE, &fCurrentNoNet );
if (err != NERR_Success) {
HandleError( hDlg, err );
::SetWindowLong(hDlg, DWL_MSGRESULT, TRUE); // TRUE if error
break;
}
}
pprofext->fNoNet = fCurrentNoNet;
::SetWindowLong(hDlg, DWL_MSGRESULT, FALSE); // TRUE if error
}
break;
case PSN_RESET:
//
// user canceled the property sheet
//
break;
}
break;
case WM_HELP:
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, HELP_FILE,
HELP_WM_HELP, (DWORD)(LPTSTR)HwProfileHelpIds);
break;
case WM_CONTEXTMENU:
WinHelp((HWND)wParam, HELP_FILE, HELP_CONTEXTMENU,
(DWORD)(LPTSTR)HwProfileHelpIds);
break;
case WM_COMMAND:
default:
return FALSE;
break;
}
return TRUE;
} // NetworkPropDlg