Unloading .Net component from Explorer.exe

J

jwallison

I have a .Net-based assembly that implements an Explorer Browser Helper
Object (BHO).

Once it is loaded by a non-.Net EXE, can it be unloaded without
shutting-down the EXE (Explorer)?
Can I programmatically prevent it from loading in the first place?

The last time I visited this problem, I ended-up writing a Win32 DLL that
checked to see if it was invoked by "explorer"; if it was, it unloaded, if
not, it invoked the "real" .Net assembly implementing the browser helper
object.. Ugly as sin, a pain to deploy and maintain.

Is there a (better) way to do this?
 
P

Peter Huang

Hi Jim,

I think the BHO's lifetime is controled by shell(explorer.exe), shell will
decide when to load and unload the dll.
If you want to prevent the BHO from loading at the first time, I think we
can just delete the registry key under the registry key below or unregister
the dll.
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper
Objects

When an instance of Internet Explorer 4.0 is started, it looks in the
registry for CLSIDs stored under the following key:
HKLM\SOFTWARE\Microsoft\Windows \CurrentVersion\Explorer\Browser Helper
Objects

If this key exists and there are CLSIDs listed under it, Internet Explorer
will use CoCreateInstance() to try to create an instance of each object
listed as a subkey under this key. Note that the CLSID of the object you
create must be listed as a subkey and not a named value.

For example, imagine that the following entry exists in the registry,
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper
Objects\{CE7C3CF0-4B15-11D1-ABED-709549C10000}. Internet Explorer will try
to create an instance of the object pointed to by the CLSID {CE7C3CF0-4B15-
11D1-ABED-709549C10000} each time Internet Explorer starts.

These registry keys can be created manually or through the self
registration portion of your COM application.

SAMPLE: IEHelper-Attaching to Internet Explorer 4.0 by Using a Browser
Helper Object
http://support.microsoft.com/?id=179230

For your workaround that use another dll to force unloading the BHO dll
which is ugly also I think that may cause problem.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
J

jwallison

explorer.exe and iexplore.exe both load any registered BHO when they are
launched; however, it it is not always desirable (or valid) for the BHO to
be loaded in both cases. My customer wishes to load the BHO in iexplore, but
not in explorer, if for no other reason than to not load the CLR runtime
into explorer when it is not needed.

The Win32 DLL solution "works"; it just isn't a .Net solution.
 
P

Peter Huang

Hi Jim,

Now I am researching the issue and I will update you with new information
ASAP.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
P

Peter Huang

Hi Jim,

Based on my further research, as you have know, a BHO is a COM server and
should be registered both as a COM server and as a BHO. The ATL Wizard
provides you with the necessary registrar script code (RGS) that
accomplishes the first task. What follows is the RGS code that properly
installs a helper object. (The CLSID comes from the example.)
HKLM {
SOFTWARE {
Microsoft {
Windows {
CurrentVersion {
Explorer {
'Browser Helper Objects' {
ForceRemove {1E1B2879-88FF-11D2-8D96-D7ACAC95951F}
}}}}}}}
Note the ForceRemove clause that causes the key to be removed when you
unregister the object.
Under the Browser Helper Objects key fall all the installed helper objects.
Such a list is never cached by the browser, so installing and testing BHOs
is really a quick matter.
Windows XP / Server 2003: A Browser Helper Object can specify that it
should be loaded only by Internet Explorer and not by Windows Explorer. To
do this, create a blank string value named "NoExplorer" in the CLSID
registration under the Browser Helper Objects key. Here is the above
example adjusted so that it applies only to Internet Explorer:

HKLM {
SOFTWARE {
Microsoft {
Windows {
CurrentVersion {
Explorer {
'Browser Helper Objects' {
ForceRemove {1E1B2879-88FF-11D2-8D96-D7ACAC95951F} {
val NoExplorer = s ""
}}}}}}}}


You may have a try and let me know if that works for you.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
J

jwallison

Is there a solution that works on 2000, 98 and ME...? Existing product
based on Visual C++ supports those platforms, customer wants the same in
..Net-based product.
 
P

Peter Huang

Hi Jim,

I will try to keep researching and I will update you with new information
ASAP.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
P

Peter Huang

Hi,

Now I am involving another engineer in this field,
Thank you for your understanding!

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
J

Jeff Layman

Hello. My name is Jeff Layman and I'm with Microsoft Developer Support.
Sorry for the late reply to this issue.

It is not possible to unload the .NET Framework once its been loaded, and
so you're still going to have to use an intermediate Win32 BHO, and use
that to determine whether you've been loaded by iexplorer.exe or
explorer.exe. If the Win32 BHO determines that it has been loaded by
explorer.exe, then it can go ahead and create an instance of the real .NET
helper object using the normal COM functionality, assuming that the .NET
helper object has been registered as a COM object on the machine. The
Win32 BHO should implement stubs for the interfaces that explorer.exe will
expect the helper object to implement, and these wrappers simply call into
the instance of .NET helper object.

The IEHelper example is available from:

http://support.microsoft.com/default.aspx?scid=kb;en-us;179230

In order to determine the application that loaded the Win32 BHO and to make
that determination under any version of Windows from 95 up to the current,
I used information available at:

http://support.microsoft.com/default.aspx?scid=kb;en-us;175030


Below is the code from my modified version of IEHlprObj.cpp. The added
code is in the ProcessIsInternetExplorer function. This function returns
TRUE if IEHelper.DLL was loaded by iexplorer.exe, and it returns FALSE if
not. Note that I have to #include tlhelp32.h and vdmdbg.h to get this to
build. The call to ProcessIsInternetExplorer is made from SetSite.

////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////
// IEHlprObj.cpp : Implementation of CIEHlprObj
#include "stdafx.h"
#include "IEHelper.h"
#include "IEHlprObj.h"
#include "ExDispID.h"
#include <strstrea.h>

#include <tlhelp32.h> // required by ProcessIsInternetExplorer
#include <vdmdbg.h> // required by ProcessIsInternetExplorer


const char* pszAppName = "IEHelper";



////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/////////////////////////
// the following code was copied and modified from KB175030 - "How To
Enumerate Applications Using Win32 APIs"

//
// EnumProc.c
//

typedef BOOL (CALLBACK *PROCENUMPROC)(DWORD, WORD, LPSTR, LPARAM);

typedef struct
{
DWORD dwPID;
PROCENUMPROC lpProc;
DWORD lParam;
BOOL bEnd;
} EnumInfoStruct;

BOOL WINAPI ProcessIsInternetExplorer( PROCENUMPROC lpProc,
LPARAM lParam,
DWORD dwTargetProcessID,
HANDLE hTargetProcessHandle);

BOOL WINAPI Enum16(DWORD dwThreadId, WORD hMod16, WORD hTask16,
PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined);

//
// The EnumProcs function takes a pointer to a callback function
// that will be called once per process with the process filename
// and process ID.
//
// lpProc -- Address of callback routine.
//
// lParam -- A user-defined LPARAM value to be passed to
// the callback routine.
//
// Callback function definition:
// BOOL CALLBACK Proc(DWORD dw, WORD w, LPCSTR lpstr, LPARAM lParam);
//
BOOL WINAPI ProcessIsInternetExplorer( PROCENUMPROC lpProc,
LPARAM lParam,
DWORD dwTargetProcessID,
HANDLE hTargetProcessHandle)
{
OSVERSIONINFO osver;
HINSTANCE hInstLib = NULL;
HINSTANCE hInstLib2 = NULL;
HANDLE hSnapShot = NULL;
LPDWORD lpdwPIDs = NULL;
PROCESSENTRY32 procentry;
BOOL bFlag;
DWORD dwSize;
DWORD dwSize2;
DWORD dwIndex;
HMODULE hMod;
HANDLE hProcess;
char szFileName[MAX_PATH];
EnumInfoStruct sInfo;

// ToolHelp Function Pointers.
HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD, DWORD);
BOOL (WINAPI *lpfProcess32First)(HANDLE, LPPROCESSENTRY32);
BOOL (WINAPI *lpfProcess32Next)(HANDLE, LPPROCESSENTRY32);

// PSAPI Function Pointers.
BOOL (WINAPI *lpfEnumProcesses)(DWORD *, DWORD, DWORD *);
BOOL (WINAPI *lpfEnumProcessModules)(HANDLE, HMODULE *, DWORD,
LPDWORD);
DWORD (WINAPI *lpfGetModuleBaseName)(HANDLE, HMODULE, LPTSTR, DWORD);

// VDMDBG Function Pointers.
INT (WINAPI *lpfVDMEnumTaskWOWEx)(DWORD, TASKENUMPROCEX, LPARAM);

// Retrieve the OS version
osver.dwOSVersionInfoSize = sizeof(osver);
if (!GetVersionEx(&osver))
return FALSE;

// If Windows NT 4.0
if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT && osver.dwMajorVersion
== 4)
{
__try
{

// Get the procedure addresses explicitly. We do
// this so we don't have to worry about modules
// failing to load under OSes other than Windows NT 4.0
// because references to PSAPI.DLL can't be resolved.
hInstLib = LoadLibraryA("PSAPI.DLL");
if (hInstLib == NULL)
__leave;

hInstLib2 = LoadLibraryA("VDMDBG.DLL");
if (hInstLib2 == NULL)
__leave;

// Get procedure addresses.
lpfEnumProcesses = (BOOL (WINAPI *)(DWORD *, DWORD, DWORD*))
GetProcAddress(hInstLib, "EnumProcesses");

lpfEnumProcessModules = (BOOL (WINAPI *)(HANDLE, HMODULE *,
DWORD, LPDWORD)) GetProcAddress(hInstLib,
"EnumProcessModules");

lpfGetModuleBaseName = (DWORD (WINAPI *)(HANDLE, HMODULE,
LPTSTR, DWORD)) GetProcAddress(hInstLib,
"GetModuleBaseNameA");

lpfVDMEnumTaskWOWEx = (INT (WINAPI *)(DWORD, TASKENUMPROCEX,
LPARAM)) GetProcAddress(hInstLib2, "VDMEnumTaskWOWEx");

if (lpfEnumProcesses == NULL
|| lpfEnumProcessModules == NULL
|| lpfGetModuleBaseName == NULL
|| lpfVDMEnumTaskWOWEx == NULL)
__leave;

//
// Call the PSAPI function EnumProcesses to get all of the
// ProcID's currently in the system.
//
// NOTE: In the documentation, the third parameter of
// EnumProcesses is named cbNeeded, which implies that you
// can call the function once to find out how much space to
// allocate for a buffer and again to fill the buffer.
// This is not the case. The cbNeeded parameter returns
// the number of PIDs returned, so if your buffer size is
// zero cbNeeded returns zero.
//
// NOTE: The "HeapAlloc" loop here ensures that we
// actually allocate a buffer large enough for all the
// PIDs in the system.
//
dwSize2 = 256 * sizeof(DWORD);
do {

if (lpdwPIDs) {
HeapFree(GetProcessHeap(), 0, lpdwPIDs);
dwSize2 *= 2;
}

lpdwPIDs = (LPDWORD) HeapAlloc(GetProcessHeap(), 0,
dwSize2);
if (lpdwPIDs == NULL)
__leave;

if (!lpfEnumProcesses(lpdwPIDs, dwSize2, &dwSize))
__leave;

} while (dwSize == dwSize2);

// How many ProcID's did we get?
dwSize /= sizeof(DWORD);

// Loop through each ProcID.
for (dwIndex = 0; dwIndex < dwSize; dwIndex++) {

szFileName[0] = 0;

// Open the process (if we can... security does not
// permit every process in the system to be opened).
hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE, lpdwPIDs[dwIndex]);
if (hProcess != NULL) {

// Here we call EnumProcessModules to get only the
// first module in the process. This will be the
// EXE module for which we will retrieve the name.
if (lpfEnumProcessModules(hProcess, &hMod,
sizeof(hMod), &dwSize2)) {

// Get the module name
if (!lpfGetModuleBaseName(hProcess, hMod,
szFileName, sizeof(szFileName)))
szFileName[0] = 0;
}
CloseHandle(hProcess);
}
// Regardless of OpenProcess success or failure, we
// still call the enum func with the ProcID.
if (!lpProc(lpdwPIDs[dwIndex], 0, szFileName, lParam))
break;

// Did we just bump into an NTVDM?
if (_stricmp(szFileName, "NTVDM.EXE") == 0) {

// Fill in some info for the 16-bit enum proc.
sInfo.dwPID = lpdwPIDs[dwIndex];
sInfo.lpProc = lpProc;
sInfo.lParam = (DWORD) lParam;
sInfo.bEnd = FALSE;

// Enum the 16-bit stuff.
lpfVDMEnumTaskWOWEx(lpdwPIDs[dwIndex],
(TASKENUMPROCEX) Enum16, (LPARAM) &sInfo);

// Did our main enum func say quit?
if (sInfo.bEnd)
break;
}
}

} __finally
{

if (hInstLib)
FreeLibrary(hInstLib);

if (hInstLib2)
FreeLibrary(hInstLib2);

if (lpdwPIDs)
HeapFree(GetProcessHeap(), 0, lpdwPIDs);
}

// If any OS other than Windows NT 4.0.
}
else if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
|| (osver.dwPlatformId == VER_PLATFORM_WIN32_NT
&& osver.dwMajorVersion > 4))
{

__try
{

hInstLib = LoadLibraryA("Kernel32.DLL");
if (hInstLib == NULL)
__leave;

// If NT-based OS, load VDMDBG.DLL.
if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
hInstLib2 = LoadLibraryA("VDMDBG.DLL");
if (hInstLib2 == NULL)
__leave;
}

// Get procedure addresses. We are linking to
// these functions explicitly, because a module using
// this code would fail to load under Windows NT,
// which does not have the Toolhelp32
// functions in KERNEL32.DLL.
lpfCreateToolhelp32Snapshot =
(HANDLE (WINAPI *)(DWORD,DWORD))
GetProcAddress(hInstLib, "CreateToolhelp32Snapshot");

lpfProcess32First =
(BOOL (WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress(hInstLib, "Process32First");

lpfProcess32Next =
(BOOL (WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress(hInstLib, "Process32Next");

if (lpfProcess32Next == NULL
|| lpfProcess32First == NULL
|| lpfCreateToolhelp32Snapshot == NULL)
__leave;

if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
lpfVDMEnumTaskWOWEx = (INT (WINAPI *)(DWORD, TASKENUMPROCEX,
LPARAM)) GetProcAddress(hInstLib2, "VDMEnumTaskWOWEx");
if (lpfVDMEnumTaskWOWEx == NULL)
__leave;
}

// Get a handle to a Toolhelp snapshot of all processes.
hSnapShot = lpfCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapShot == INVALID_HANDLE_VALUE)
{
FreeLibrary(hInstLib);
return FALSE;
}

// Get the first process' information.
procentry.dwSize = sizeof(PROCESSENTRY32);
bFlag = lpfProcess32First(hSnapShot, &procentry);

// While there are processes, keep looping.
while (bFlag)
{
// Call the enum func with the filename and ProcID.
if ( lpProc(procentry.th32ProcessID, 0, procentry.szExeFile,
lParam) )
{
// Did we just bump into an NTVDM?
if (_stricmp(procentry.szExeFile, "NTVDM.EXE") == 0)
{
// Fill in some info for the 16-bit enum proc.
sInfo.dwPID = procentry.th32ProcessID;
sInfo.lpProc = lpProc;
sInfo.lParam = (DWORD) lParam;
sInfo.bEnd = FALSE;

// Enum the 16-bit stuff.
lpfVDMEnumTaskWOWEx(procentry.th32ProcessID,
(TASKENUMPROCEX) Enum16, (LPARAM) &sInfo);

// Did our main enum func say quit?
if (sInfo.bEnd)
break;
}

/*
typedef struct tagPROCESSENTRY32
{
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
} PROCESSENTRY32;
*/

if (procentry.th32ProcessID == dwTargetProcessID)
{
if (_tcsstr(procentry.szExeFile,"iexplore.exe") != NULL)
return TRUE;
}

procentry.dwSize = sizeof(PROCESSENTRY32);
bFlag = lpfProcess32Next(hSnapShot, &procentry);

}
else
{
bFlag = FALSE;
}

} // while (bFlag)

}
__finally
{

if (hInstLib)
FreeLibrary(hInstLib);

if (hInstLib2)
FreeLibrary(hInstLib2);
}

}
else
{
return FALSE;
}

// Free the library.
FreeLibrary(hInstLib);

return TRUE;
}


BOOL WINAPI Enum16(DWORD dwThreadId, WORD hMod16, WORD hTask16, PSZ
pszModName, PSZ pszFileName, LPARAM lpUserDefined)
{

BOOL bRet;

EnumInfoStruct *psInfo = (EnumInfoStruct *)lpUserDefined;

bRet = psInfo->lpProc(psInfo->dwPID, hTask16, pszFileName,
psInfo->lParam);

if (!bRet)
psInfo->bEnd = TRUE;

return !bRet;
}


BOOL CALLBACK MyProcessEnumerator(DWORD dwPID, WORD wTask, LPCSTR
szProcess, LPARAM lParam)
{

if (wTask == 0)
printf("%5u %s\n", dwPID, szProcess);
else
printf(" %5u %s\n", wTask, szProcess);

return TRUE;
}
//
// end - copy from KB article 175030.
//////////////////////////////////////////////////////////////



////////////////////////////////////////////////////////////////////////////
/
// CIEHlprObj

//
// CIEHlprObj Methods
//

CIEHlprObj::~CIEHlprObj()
{
}

BOOL CIEHlprObj::ManageConnection(enum ConnectType eConnectType)
{
if (!m_spWebBrowser2)
return S_OK;

HRESULT hr;

//
// If eConnectType is Advise then we are advising IE that we
// want to handle events. If eConnectType is Unadvise, we are
// telling IE that we no longer want to handle events.
//
CComQIPtr<IConnectionPointContainer,
&IID_IConnectionPointContainer> spCPContainer(m_spWebBrowser2);

if (spCPContainer != NULL)
{
CComPtr<IConnectionPoint> spConnectionPoint;

hr = spCPContainer->FindConnectionPoint(DIID_DWebBrowserEvents2,
&spConnectionPoint);
if (SUCCEEDED(hr))
{
if (eConnectType == Advise)
{
//
// Advise the client site of our desire to be handle events
//
hr = spConnectionPoint->Advise((IDispatch*)this, &m_dwCookie);
if (FAILED(hr))
ATLTRACE("\n%s: ManageConnection(): Failed to Advise\n\n",
pszAppName);
}
else
{
// Remove us from the list of people interested...
hr = spConnectionPoint->Unadvise(m_dwCookie);
if (FAILED(hr))
ATLTRACE("\npszAppName: ManageConnection(): Failed to
Unadvise\n\n", pszAppName);
}
}
}

return (SUCCEEDED(hr));
}

//
// IOleObjectWithSite Methods
//
STDMETHODIMP CIEHlprObj::SetSite(IUnknown *pUnkSite)
{
USES_CONVERSION;

if (!pUnkSite)
ATLTRACE("\nSetSite(): pUnkSite is NULL\n\n");
else
{
BOOL bIsExplorer = FALSE;
// get our process id.
DWORD procID = GetCurrentProcessId();
if (procID)
{
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ, FALSE, procID );
if (hProcess)
bIsExplorer = ProcessIsInternetExplorer((PROCENUMPROC)
MyProcessEnumerator, 0, procID, hProcess);
}

if (bIsExplorer)
{
// invoke the real .NET BOH here.

//CoCreateInstance(...

}

// Query pUnkSite for the IWebBrowser2 interface.
m_spWebBrowser2 = pUnkSite;

if (m_spWebBrowser2)
{
// Create the events dialog box
//m_dlgEvents.Create:):GetDesktopWindow());
//m_dlgEvents.ShowWindow(SW_SHOWNORMAL);

// Connect to the browser in order to handle events.
if (!ManageConnection(Advise))
::MessageBox(NULL, _T("Failure sinking events from
IWebBrowser2"), (const char *)pszAppName, MB_OK);
}
}

return S_OK;
}

////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

That's what I've got for you. I hope this is information you can use.

Thank you,
Jeff Layman
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
J

jwallison

Thanks for your feedback

I found a little dll stub program a couple of years ago called
"wrapmscoree.dll" that's only 30 or so lines of code.

wrapmscoree.dll calls GetModuleFileName() to determine the procname of the
process attempting to load the DLL- if the loading process is not IEXPLORE,
DllMain returns FALSE; if loader == IEXPLORE, it does a LoadLibrary() on
mscoree.dll (to load the CLR/invoke the .Net BHO) and return TRUE.

To invoke wrapmscoree.dll code, merely point the registry entry for the BHO
to wrapmscoree.dll in the BHO's InProcServer32 entry rather than
mscoree.dll.

I was hoping that there was a "cleaner" (something that didn't involve
Win32) way than either that approach or the one you provided, but thanks
for the info.



Jeff Layman said:
Hello. My name is Jeff Layman and I'm with Microsoft Developer Support.
Sorry for the late reply to this issue.

It is not possible to unload the .NET Framework once its been loaded, and
so you're still going to have to use an intermediate Win32 BHO, and use
that to determine whether you've been loaded by iexplorer.exe or
explorer.exe. If the Win32 BHO determines that it has been loaded by
explorer.exe, then it can go ahead and create an instance of the real .NET
helper object using the normal COM functionality, assuming that the .NET
helper object has been registered as a COM object on the machine. The
Win32 BHO should implement stubs for the interfaces that explorer.exe will
expect the helper object to implement, and these wrappers simply call into
the instance of .NET helper object.

The IEHelper example is available from:

http://support.microsoft.com/default.aspx?scid=kb;en-us;179230

In order to determine the application that loaded the Win32 BHO and to make
that determination under any version of Windows from 95 up to the current,
I used information available at:

http://support.microsoft.com/default.aspx?scid=kb;en-us;175030


Below is the code from my modified version of IEHlprObj.cpp. The added
code is in the ProcessIsInternetExplorer function. This function returns
TRUE if IEHelper.DLL was loaded by iexplorer.exe, and it returns FALSE if
not. Note that I have to #include tlhelp32.h and vdmdbg.h to get this to
build. The call to ProcessIsInternetExplorer is made from SetSite.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////
// IEHlprObj.cpp : Implementation of CIEHlprObj
#include "stdafx.h"
#include "IEHelper.h"
#include "IEHlprObj.h"
#include "ExDispID.h"
#include <strstrea.h>

#include <tlhelp32.h> // required by ProcessIsInternetExplorer
#include <vdmdbg.h> // required by ProcessIsInternetExplorer


const char* pszAppName = "IEHelper";



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////
// the following code was copied and modified from KB175030 - "How To
Enumerate Applications Using Win32 APIs"

//
// EnumProc.c
//

typedef BOOL (CALLBACK *PROCENUMPROC)(DWORD, WORD, LPSTR, LPARAM);

typedef struct
{
DWORD dwPID;
PROCENUMPROC lpProc;
DWORD lParam;
BOOL bEnd;
} EnumInfoStruct;

BOOL WINAPI ProcessIsInternetExplorer( PROCENUMPROC lpProc,
LPARAM lParam,
DWORD dwTargetProcessID,
HANDLE hTargetProcessHandle);

BOOL WINAPI Enum16(DWORD dwThreadId, WORD hMod16, WORD hTask16,
PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined);

//
// The EnumProcs function takes a pointer to a callback function
// that will be called once per process with the process filename
// and process ID.
//
// lpProc -- Address of callback routine.
//
// lParam -- A user-defined LPARAM value to be passed to
// the callback routine.
//
// Callback function definition:
// BOOL CALLBACK Proc(DWORD dw, WORD w, LPCSTR lpstr, LPARAM lParam);
//
BOOL WINAPI ProcessIsInternetExplorer( PROCENUMPROC lpProc,
LPARAM lParam,
DWORD dwTargetProcessID,
HANDLE hTargetProcessHandle)
{
OSVERSIONINFO osver;
HINSTANCE hInstLib = NULL;
HINSTANCE hInstLib2 = NULL;
HANDLE hSnapShot = NULL;
LPDWORD lpdwPIDs = NULL;
PROCESSENTRY32 procentry;
BOOL bFlag;
DWORD dwSize;
DWORD dwSize2;
DWORD dwIndex;
HMODULE hMod;
HANDLE hProcess;
char szFileName[MAX_PATH];
EnumInfoStruct sInfo;

// ToolHelp Function Pointers.
HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD, DWORD);
BOOL (WINAPI *lpfProcess32First)(HANDLE, LPPROCESSENTRY32);
BOOL (WINAPI *lpfProcess32Next)(HANDLE, LPPROCESSENTRY32);

// PSAPI Function Pointers.
BOOL (WINAPI *lpfEnumProcesses)(DWORD *, DWORD, DWORD *);
BOOL (WINAPI *lpfEnumProcessModules)(HANDLE, HMODULE *, DWORD,
LPDWORD);
DWORD (WINAPI *lpfGetModuleBaseName)(HANDLE, HMODULE, LPTSTR, DWORD);

// VDMDBG Function Pointers.
INT (WINAPI *lpfVDMEnumTaskWOWEx)(DWORD, TASKENUMPROCEX, LPARAM);

// Retrieve the OS version
osver.dwOSVersionInfoSize = sizeof(osver);
if (!GetVersionEx(&osver))
return FALSE;

// If Windows NT 4.0
if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT && osver.dwMajorVersion
== 4)
{
__try
{

// Get the procedure addresses explicitly. We do
// this so we don't have to worry about modules
// failing to load under OSes other than Windows NT 4.0
// because references to PSAPI.DLL can't be resolved.
hInstLib = LoadLibraryA("PSAPI.DLL");
if (hInstLib == NULL)
__leave;

hInstLib2 = LoadLibraryA("VDMDBG.DLL");
if (hInstLib2 == NULL)
__leave;

// Get procedure addresses.
lpfEnumProcesses = (BOOL (WINAPI *)(DWORD *, DWORD, DWORD*))
GetProcAddress(hInstLib, "EnumProcesses");

lpfEnumProcessModules = (BOOL (WINAPI *)(HANDLE, HMODULE *,
DWORD, LPDWORD)) GetProcAddress(hInstLib,
"EnumProcessModules");

lpfGetModuleBaseName = (DWORD (WINAPI *)(HANDLE, HMODULE,
LPTSTR, DWORD)) GetProcAddress(hInstLib,
"GetModuleBaseNameA");

lpfVDMEnumTaskWOWEx = (INT (WINAPI *)(DWORD, TASKENUMPROCEX,
LPARAM)) GetProcAddress(hInstLib2, "VDMEnumTaskWOWEx");

if (lpfEnumProcesses == NULL
|| lpfEnumProcessModules == NULL
|| lpfGetModuleBaseName == NULL
|| lpfVDMEnumTaskWOWEx == NULL)
__leave;

//
// Call the PSAPI function EnumProcesses to get all of the
// ProcID's currently in the system.
//
// NOTE: In the documentation, the third parameter of
// EnumProcesses is named cbNeeded, which implies that you
// can call the function once to find out how much space to
// allocate for a buffer and again to fill the buffer.
// This is not the case. The cbNeeded parameter returns
// the number of PIDs returned, so if your buffer size is
// zero cbNeeded returns zero.
//
// NOTE: The "HeapAlloc" loop here ensures that we
// actually allocate a buffer large enough for all the
// PIDs in the system.
//
dwSize2 = 256 * sizeof(DWORD);
do {

if (lpdwPIDs) {
HeapFree(GetProcessHeap(), 0, lpdwPIDs);
dwSize2 *= 2;
}

lpdwPIDs = (LPDWORD) HeapAlloc(GetProcessHeap(), 0,
dwSize2);
if (lpdwPIDs == NULL)
__leave;

if (!lpfEnumProcesses(lpdwPIDs, dwSize2, &dwSize))
__leave;

} while (dwSize == dwSize2);

// How many ProcID's did we get?
dwSize /= sizeof(DWORD);

// Loop through each ProcID.
for (dwIndex = 0; dwIndex < dwSize; dwIndex++) {

szFileName[0] = 0;

// Open the process (if we can... security does not
// permit every process in the system to be opened).
hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE, lpdwPIDs[dwIndex]);
if (hProcess != NULL) {

// Here we call EnumProcessModules to get only the
// first module in the process. This will be the
// EXE module for which we will retrieve the name.
if (lpfEnumProcessModules(hProcess, &hMod,
sizeof(hMod), &dwSize2)) {

// Get the module name
if (!lpfGetModuleBaseName(hProcess, hMod,
szFileName, sizeof(szFileName)))
szFileName[0] = 0;
}
CloseHandle(hProcess);
}
// Regardless of OpenProcess success or failure, we
// still call the enum func with the ProcID.
if (!lpProc(lpdwPIDs[dwIndex], 0, szFileName, lParam))
break;

// Did we just bump into an NTVDM?
if (_stricmp(szFileName, "NTVDM.EXE") == 0) {

// Fill in some info for the 16-bit enum proc.
sInfo.dwPID = lpdwPIDs[dwIndex];
sInfo.lpProc = lpProc;
sInfo.lParam = (DWORD) lParam;
sInfo.bEnd = FALSE;

// Enum the 16-bit stuff.
lpfVDMEnumTaskWOWEx(lpdwPIDs[dwIndex],
(TASKENUMPROCEX) Enum16, (LPARAM) &sInfo);

// Did our main enum func say quit?
if (sInfo.bEnd)
break;
}
}

} __finally
{

if (hInstLib)
FreeLibrary(hInstLib);

if (hInstLib2)
FreeLibrary(hInstLib2);

if (lpdwPIDs)
HeapFree(GetProcessHeap(), 0, lpdwPIDs);
}

// If any OS other than Windows NT 4.0.
}
else if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
|| (osver.dwPlatformId == VER_PLATFORM_WIN32_NT
&& osver.dwMajorVersion > 4))
{

__try
{

hInstLib = LoadLibraryA("Kernel32.DLL");
if (hInstLib == NULL)
__leave;

// If NT-based OS, load VDMDBG.DLL.
if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
hInstLib2 = LoadLibraryA("VDMDBG.DLL");
if (hInstLib2 == NULL)
__leave;
}

// Get procedure addresses. We are linking to
// these functions explicitly, because a module using
// this code would fail to load under Windows NT,
// which does not have the Toolhelp32
// functions in KERNEL32.DLL.
lpfCreateToolhelp32Snapshot =
(HANDLE (WINAPI *)(DWORD,DWORD))
GetProcAddress(hInstLib, "CreateToolhelp32Snapshot");

lpfProcess32First =
(BOOL (WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress(hInstLib, "Process32First");

lpfProcess32Next =
(BOOL (WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress(hInstLib, "Process32Next");

if (lpfProcess32Next == NULL
|| lpfProcess32First == NULL
|| lpfCreateToolhelp32Snapshot == NULL)
__leave;

if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
lpfVDMEnumTaskWOWEx = (INT (WINAPI *)(DWORD, TASKENUMPROCEX,
LPARAM)) GetProcAddress(hInstLib2, "VDMEnumTaskWOWEx");
if (lpfVDMEnumTaskWOWEx == NULL)
__leave;
}

// Get a handle to a Toolhelp snapshot of all processes.
hSnapShot = lpfCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapShot == INVALID_HANDLE_VALUE)
{
FreeLibrary(hInstLib);
return FALSE;
}

// Get the first process' information.
procentry.dwSize = sizeof(PROCESSENTRY32);
bFlag = lpfProcess32First(hSnapShot, &procentry);

// While there are processes, keep looping.
while (bFlag)
{
// Call the enum func with the filename and ProcID.
if ( lpProc(procentry.th32ProcessID, 0, procentry.szExeFile,
lParam) )
{
// Did we just bump into an NTVDM?
if (_stricmp(procentry.szExeFile, "NTVDM.EXE") == 0)
{
// Fill in some info for the 16-bit enum proc.
sInfo.dwPID = procentry.th32ProcessID;
sInfo.lpProc = lpProc;
sInfo.lParam = (DWORD) lParam;
sInfo.bEnd = FALSE;

// Enum the 16-bit stuff.
lpfVDMEnumTaskWOWEx(procentry.th32ProcessID,
(TASKENUMPROCEX) Enum16, (LPARAM) &sInfo);

// Did our main enum func say quit?
if (sInfo.bEnd)
break;
}

/*
typedef struct tagPROCESSENTRY32
{
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
} PROCESSENTRY32;
*/

if (procentry.th32ProcessID == dwTargetProcessID)
{
if (_tcsstr(procentry.szExeFile,"iexplore.exe") != NULL)
return TRUE;
}

procentry.dwSize = sizeof(PROCESSENTRY32);
bFlag = lpfProcess32Next(hSnapShot, &procentry);

}
else
{
bFlag = FALSE;
}

} // while (bFlag)

}
__finally
{

if (hInstLib)
FreeLibrary(hInstLib);

if (hInstLib2)
FreeLibrary(hInstLib2);
}

}
else
{
return FALSE;
}

// Free the library.
FreeLibrary(hInstLib);

return TRUE;
}


BOOL WINAPI Enum16(DWORD dwThreadId, WORD hMod16, WORD hTask16, PSZ
pszModName, PSZ pszFileName, LPARAM lpUserDefined)
{

BOOL bRet;

EnumInfoStruct *psInfo = (EnumInfoStruct *)lpUserDefined;

bRet = psInfo->lpProc(psInfo->dwPID, hTask16, pszFileName,
psInfo->lParam);

if (!bRet)
psInfo->bEnd = TRUE;

return !bRet;
}


BOOL CALLBACK MyProcessEnumerator(DWORD dwPID, WORD wTask, LPCSTR
szProcess, LPARAM lParam)
{

if (wTask == 0)
printf("%5u %s\n", dwPID, szProcess);
else
printf(" %5u %s\n", wTask, szProcess);

return TRUE;
}
//
// end - copy from KB article 175030.
//////////////////////////////////////////////////////////////



////////////////////////////////////////////////////////////////////////////
/
// CIEHlprObj

//
// CIEHlprObj Methods
//

CIEHlprObj::~CIEHlprObj()
{
}

BOOL CIEHlprObj::ManageConnection(enum ConnectType eConnectType)
{
if (!m_spWebBrowser2)
return S_OK;

HRESULT hr;

//
// If eConnectType is Advise then we are advising IE that we
// want to handle events. If eConnectType is Unadvise, we are
// telling IE that we no longer want to handle events.
//
CComQIPtr<IConnectionPointContainer,
&IID_IConnectionPointContainer> spCPContainer(m_spWebBrowser2);

if (spCPContainer != NULL)
{
CComPtr<IConnectionPoint> spConnectionPoint;

hr = spCPContainer->FindConnectionPoint(DIID_DWebBrowserEvents2,
&spConnectionPoint);
if (SUCCEEDED(hr))
{
if (eConnectType == Advise)
{
//
// Advise the client site of our desire to be handle events
//
hr = spConnectionPoint->Advise((IDispatch*)this, &m_dwCookie);
if (FAILED(hr))
ATLTRACE("\n%s: ManageConnection(): Failed to Advise\n\n",
pszAppName);
}
else
{
// Remove us from the list of people interested...
hr = spConnectionPoint->Unadvise(m_dwCookie);
if (FAILED(hr))
ATLTRACE("\npszAppName: ManageConnection(): Failed to
Unadvise\n\n", pszAppName);
}
}
}

return (SUCCEEDED(hr));
}

//
// IOleObjectWithSite Methods
//
STDMETHODIMP CIEHlprObj::SetSite(IUnknown *pUnkSite)
{
USES_CONVERSION;

if (!pUnkSite)
ATLTRACE("\nSetSite(): pUnkSite is NULL\n\n");
else
{
BOOL bIsExplorer = FALSE;
// get our process id.
DWORD procID = GetCurrentProcessId();
if (procID)
{
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ, FALSE, procID );
if (hProcess)
bIsExplorer = ProcessIsInternetExplorer((PROCENUMPROC)
MyProcessEnumerator, 0, procID, hProcess);
}

if (bIsExplorer)
{
// invoke the real .NET BOH here.

//CoCreateInstance(...

}

// Query pUnkSite for the IWebBrowser2 interface.
m_spWebBrowser2 = pUnkSite;

if (m_spWebBrowser2)
{
// Create the events dialog box
//m_dlgEvents.Create:):GetDesktopWindow());
//m_dlgEvents.ShowWindow(SW_SHOWNORMAL);

// Connect to the browser in order to handle events.
if (!ManageConnection(Advise))
::MessageBox(NULL, _T("Failure sinking events from
IWebBrowser2"), (const char *)pszAppName, MB_OK);
}
}

return S_OK;
}

////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

That's what I've got for you. I hope this is information you can use.

Thank you,
Jeff Layman
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
J

jwallison

Here's the source to wrapmscoree.dll, has one other "part" I didn't
mention...

// wrapmscoree.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"

HINSTANCE hLib;

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID
/*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
TCHAR Loader[MAX_PATH];

GetModuleFileName( NULL, Loader, MAX_PATH);
for ( int i = lstrlen( Loader); i > 0; i--)
if ( Loader == _T('\\'))
{
lstrcpy( Loader, Loader + i + 1);
break;
}

// allow loading of BHO by these programs only
if ( lstrcmpi( Loader, _T("iexplore.exe")))
return FALSE;

if ( ( hLib = LoadLibrary(_T("mscoree.dll"))) == NULL)
return FALSE;
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
if ( hLib)
FreeLibrary( hLib);
}
return TRUE; // ok
}

////////////////////////////////////////////////////////////////////////////
/
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
typedef HRESULT (_stdcall *fpCanUnloadNow)(void);
fpCanUnloadNow fp;
if ( hLib && ( fp = (fpCanUnloadNow)GetProcAddress( hLib,
_T("DllCanUnloadNow"))))
return fp();
return S_OK;
}

////////////////////////////////////////////////////////////////////////////
/
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
typedef HRESULT (_stdcall *fpGetClassObject)(REFCLSID rclsid, REFIID riid,
LPVOID* ppv);
fpGetClassObject fp;
if ( hLib && ( fp = (fpGetClassObject)GetProcAddress( hLib,
_T("DllGetClassObject"))))
return fp( rclsid, riid, ppv);
return E_FAIL;
}


jwallison said:
Thanks for your feedback

I found a little dll stub program a couple of years ago called
"wrapmscoree.dll" that's only 30 or so lines of code.

wrapmscoree.dll calls GetModuleFileName() to determine the procname of the
process attempting to load the DLL- if the loading process is not IEXPLORE,
DllMain returns FALSE; if loader == IEXPLORE, it does a LoadLibrary() on
mscoree.dll (to load the CLR/invoke the .Net BHO) and return TRUE.

To invoke wrapmscoree.dll code, merely point the registry entry for the BHO
to wrapmscoree.dll in the BHO's InProcServer32 entry rather than
mscoree.dll.

I was hoping that there was a "cleaner" (something that didn't involve
Win32) way than either that approach or the one you provided, but thanks
for the info.



Jeff Layman said:
Hello. My name is Jeff Layman and I'm with Microsoft Developer Support.
Sorry for the late reply to this issue.

It is not possible to unload the .NET Framework once its been loaded, and
so you're still going to have to use an intermediate Win32 BHO, and use
that to determine whether you've been loaded by iexplorer.exe or
explorer.exe. If the Win32 BHO determines that it has been loaded by
explorer.exe, then it can go ahead and create an instance of the real ..NET
helper object using the normal COM functionality, assuming that the .NET
helper object has been registered as a COM object on the machine. The
Win32 BHO should implement stubs for the interfaces that explorer.exe will
expect the helper object to implement, and these wrappers simply call into
the instance of .NET helper object.

The IEHelper example is available from:

http://support.microsoft.com/default.aspx?scid=kb;en-us;179230

In order to determine the application that loaded the Win32 BHO and to make
that determination under any version of Windows from 95 up to the current,
I used information available at:

http://support.microsoft.com/default.aspx?scid=kb;en-us;175030


Below is the code from my modified version of IEHlprObj.cpp. The added
code is in the ProcessIsInternetExplorer function. This function returns
TRUE if IEHelper.DLL was loaded by iexplorer.exe, and it returns FALSE if
not. Note that I have to #include tlhelp32.h and vdmdbg.h to get this to
build. The call to ProcessIsInternetExplorer is made from SetSite.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////
// IEHlprObj.cpp : Implementation of CIEHlprObj
#include "stdafx.h"
#include "IEHelper.h"
#include "IEHlprObj.h"
#include "ExDispID.h"
#include <strstrea.h>

#include <tlhelp32.h> // required by ProcessIsInternetExplorer
#include <vdmdbg.h> // required by ProcessIsInternetExplorer


const char* pszAppName = "IEHelper";
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////
// the following code was copied and modified from KB175030 - "How To
Enumerate Applications Using Win32 APIs"

//
// EnumProc.c
//

typedef BOOL (CALLBACK *PROCENUMPROC)(DWORD, WORD, LPSTR, LPARAM);

typedef struct
{
DWORD dwPID;
PROCENUMPROC lpProc;
DWORD lParam;
BOOL bEnd;
} EnumInfoStruct;

BOOL WINAPI ProcessIsInternetExplorer( PROCENUMPROC lpProc,
LPARAM lParam,
DWORD dwTargetProcessID,
HANDLE hTargetProcessHandle);

BOOL WINAPI Enum16(DWORD dwThreadId, WORD hMod16, WORD hTask16,
PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined);

//
// The EnumProcs function takes a pointer to a callback function
// that will be called once per process with the process filename
// and process ID.
//
// lpProc -- Address of callback routine.
//
// lParam -- A user-defined LPARAM value to be passed to
// the callback routine.
//
// Callback function definition:
// BOOL CALLBACK Proc(DWORD dw, WORD w, LPCSTR lpstr, LPARAM lParam);
//
BOOL WINAPI ProcessIsInternetExplorer( PROCENUMPROC lpProc,
LPARAM lParam,
DWORD dwTargetProcessID,
HANDLE hTargetProcessHandle)
{
OSVERSIONINFO osver;
HINSTANCE hInstLib = NULL;
HINSTANCE hInstLib2 = NULL;
HANDLE hSnapShot = NULL;
LPDWORD lpdwPIDs = NULL;
PROCESSENTRY32 procentry;
BOOL bFlag;
DWORD dwSize;
DWORD dwSize2;
DWORD dwIndex;
HMODULE hMod;
HANDLE hProcess;
char szFileName[MAX_PATH];
EnumInfoStruct sInfo;

// ToolHelp Function Pointers.
HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD, DWORD);
BOOL (WINAPI *lpfProcess32First)(HANDLE, LPPROCESSENTRY32);
BOOL (WINAPI *lpfProcess32Next)(HANDLE, LPPROCESSENTRY32);

// PSAPI Function Pointers.
BOOL (WINAPI *lpfEnumProcesses)(DWORD *, DWORD, DWORD *);
BOOL (WINAPI *lpfEnumProcessModules)(HANDLE, HMODULE *, DWORD,
LPDWORD);
DWORD (WINAPI *lpfGetModuleBaseName)(HANDLE, HMODULE, LPTSTR, DWORD);

// VDMDBG Function Pointers.
INT (WINAPI *lpfVDMEnumTaskWOWEx)(DWORD, TASKENUMPROCEX, LPARAM);

// Retrieve the OS version
osver.dwOSVersionInfoSize = sizeof(osver);
if (!GetVersionEx(&osver))
return FALSE;

// If Windows NT 4.0
if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT && osver.dwMajorVersion
== 4)
{
__try
{

// Get the procedure addresses explicitly. We do
// this so we don't have to worry about modules
// failing to load under OSes other than Windows NT 4.0
// because references to PSAPI.DLL can't be resolved.
hInstLib = LoadLibraryA("PSAPI.DLL");
if (hInstLib == NULL)
__leave;

hInstLib2 = LoadLibraryA("VDMDBG.DLL");
if (hInstLib2 == NULL)
__leave;

// Get procedure addresses.
lpfEnumProcesses = (BOOL (WINAPI *)(DWORD *, DWORD, DWORD*))
GetProcAddress(hInstLib, "EnumProcesses");

lpfEnumProcessModules = (BOOL (WINAPI *)(HANDLE, HMODULE *,
DWORD, LPDWORD)) GetProcAddress(hInstLib,
"EnumProcessModules");

lpfGetModuleBaseName = (DWORD (WINAPI *)(HANDLE, HMODULE,
LPTSTR, DWORD)) GetProcAddress(hInstLib,
"GetModuleBaseNameA");

lpfVDMEnumTaskWOWEx = (INT (WINAPI *)(DWORD, TASKENUMPROCEX,
LPARAM)) GetProcAddress(hInstLib2, "VDMEnumTaskWOWEx");

if (lpfEnumProcesses == NULL
|| lpfEnumProcessModules == NULL
|| lpfGetModuleBaseName == NULL
|| lpfVDMEnumTaskWOWEx == NULL)
__leave;

//
// Call the PSAPI function EnumProcesses to get all of the
// ProcID's currently in the system.
//
// NOTE: In the documentation, the third parameter of
// EnumProcesses is named cbNeeded, which implies that you
// can call the function once to find out how much space to
// allocate for a buffer and again to fill the buffer.
// This is not the case. The cbNeeded parameter returns
// the number of PIDs returned, so if your buffer size is
// zero cbNeeded returns zero.
//
// NOTE: The "HeapAlloc" loop here ensures that we
// actually allocate a buffer large enough for all the
// PIDs in the system.
//
dwSize2 = 256 * sizeof(DWORD);
do {

if (lpdwPIDs) {
HeapFree(GetProcessHeap(), 0, lpdwPIDs);
dwSize2 *= 2;
}

lpdwPIDs = (LPDWORD) HeapAlloc(GetProcessHeap(), 0,
dwSize2);
if (lpdwPIDs == NULL)
__leave;

if (!lpfEnumProcesses(lpdwPIDs, dwSize2, &dwSize))
__leave;

} while (dwSize == dwSize2);

// How many ProcID's did we get?
dwSize /= sizeof(DWORD);

// Loop through each ProcID.
for (dwIndex = 0; dwIndex < dwSize; dwIndex++) {

szFileName[0] = 0;

// Open the process (if we can... security does not
// permit every process in the system to be opened).
hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE, lpdwPIDs[dwIndex]);
if (hProcess != NULL) {

// Here we call EnumProcessModules to get only the
// first module in the process. This will be the
// EXE module for which we will retrieve the name.
if (lpfEnumProcessModules(hProcess, &hMod,
sizeof(hMod), &dwSize2)) {

// Get the module name
if (!lpfGetModuleBaseName(hProcess, hMod,
szFileName, sizeof(szFileName)))
szFileName[0] = 0;
}
CloseHandle(hProcess);
}
// Regardless of OpenProcess success or failure, we
// still call the enum func with the ProcID.
if (!lpProc(lpdwPIDs[dwIndex], 0, szFileName, lParam))
break;

// Did we just bump into an NTVDM?
if (_stricmp(szFileName, "NTVDM.EXE") == 0) {

// Fill in some info for the 16-bit enum proc.
sInfo.dwPID = lpdwPIDs[dwIndex];
sInfo.lpProc = lpProc;
sInfo.lParam = (DWORD) lParam;
sInfo.bEnd = FALSE;

// Enum the 16-bit stuff.
lpfVDMEnumTaskWOWEx(lpdwPIDs[dwIndex],
(TASKENUMPROCEX) Enum16, (LPARAM) &sInfo);

// Did our main enum func say quit?
if (sInfo.bEnd)
break;
}
}

} __finally
{

if (hInstLib)
FreeLibrary(hInstLib);

if (hInstLib2)
FreeLibrary(hInstLib2);

if (lpdwPIDs)
HeapFree(GetProcessHeap(), 0, lpdwPIDs);
}

// If any OS other than Windows NT 4.0.
}
else if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
|| (osver.dwPlatformId == VER_PLATFORM_WIN32_NT
&& osver.dwMajorVersion > 4))
{

__try
{

hInstLib = LoadLibraryA("Kernel32.DLL");
if (hInstLib == NULL)
__leave;

// If NT-based OS, load VDMDBG.DLL.
if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
hInstLib2 = LoadLibraryA("VDMDBG.DLL");
if (hInstLib2 == NULL)
__leave;
}

// Get procedure addresses. We are linking to
// these functions explicitly, because a module using
// this code would fail to load under Windows NT,
// which does not have the Toolhelp32
// functions in KERNEL32.DLL.
lpfCreateToolhelp32Snapshot =
(HANDLE (WINAPI *)(DWORD,DWORD))
GetProcAddress(hInstLib, "CreateToolhelp32Snapshot");

lpfProcess32First =
(BOOL (WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress(hInstLib, "Process32First");

lpfProcess32Next =
(BOOL (WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress(hInstLib, "Process32Next");

if (lpfProcess32Next == NULL
|| lpfProcess32First == NULL
|| lpfCreateToolhelp32Snapshot == NULL)
__leave;

if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
lpfVDMEnumTaskWOWEx = (INT (WINAPI *)(DWORD, TASKENUMPROCEX,
LPARAM)) GetProcAddress(hInstLib2, "VDMEnumTaskWOWEx");
if (lpfVDMEnumTaskWOWEx == NULL)
__leave;
}

// Get a handle to a Toolhelp snapshot of all processes.
hSnapShot = lpfCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapShot == INVALID_HANDLE_VALUE)
{
FreeLibrary(hInstLib);
return FALSE;
}

// Get the first process' information.
procentry.dwSize = sizeof(PROCESSENTRY32);
bFlag = lpfProcess32First(hSnapShot, &procentry);

// While there are processes, keep looping.
while (bFlag)
{
// Call the enum func with the filename and ProcID.
if ( lpProc(procentry.th32ProcessID, 0, procentry.szExeFile,
lParam) )
{
// Did we just bump into an NTVDM?
if (_stricmp(procentry.szExeFile, "NTVDM.EXE") == 0)
{
// Fill in some info for the 16-bit enum proc.
sInfo.dwPID = procentry.th32ProcessID;
sInfo.lpProc = lpProc;
sInfo.lParam = (DWORD) lParam;
sInfo.bEnd = FALSE;

// Enum the 16-bit stuff.
lpfVDMEnumTaskWOWEx(procentry.th32ProcessID,
(TASKENUMPROCEX) Enum16, (LPARAM) &sInfo);

// Did our main enum func say quit?
if (sInfo.bEnd)
break;
}

/*
typedef struct tagPROCESSENTRY32
{
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
} PROCESSENTRY32;
*/

if (procentry.th32ProcessID == dwTargetProcessID)
{
if (_tcsstr(procentry.szExeFile,"iexplore.exe") != NULL)
return TRUE;
}

procentry.dwSize = sizeof(PROCESSENTRY32);
bFlag = lpfProcess32Next(hSnapShot, &procentry);

}
else
{
bFlag = FALSE;
}

} // while (bFlag)

}
__finally
{

if (hInstLib)
FreeLibrary(hInstLib);

if (hInstLib2)
FreeLibrary(hInstLib2);
}

}
else
{
return FALSE;
}

// Free the library.
FreeLibrary(hInstLib);

return TRUE;
}


BOOL WINAPI Enum16(DWORD dwThreadId, WORD hMod16, WORD hTask16, PSZ
pszModName, PSZ pszFileName, LPARAM lpUserDefined)
{

BOOL bRet;

EnumInfoStruct *psInfo = (EnumInfoStruct *)lpUserDefined;

bRet = psInfo->lpProc(psInfo->dwPID, hTask16, pszFileName,
psInfo->lParam);

if (!bRet)
psInfo->bEnd = TRUE;

return !bRet;
}


BOOL CALLBACK MyProcessEnumerator(DWORD dwPID, WORD wTask, LPCSTR
szProcess, LPARAM lParam)
{

if (wTask == 0)
printf("%5u %s\n", dwPID, szProcess);
else
printf(" %5u %s\n", wTask, szProcess);

return TRUE;
}
//
// end - copy from KB article 175030.
//////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/
// CIEHlprObj

//
// CIEHlprObj Methods
//

CIEHlprObj::~CIEHlprObj()
{
}

BOOL CIEHlprObj::ManageConnection(enum ConnectType eConnectType)
{
if (!m_spWebBrowser2)
return S_OK;

HRESULT hr;

//
// If eConnectType is Advise then we are advising IE that we
// want to handle events. If eConnectType is Unadvise, we are
// telling IE that we no longer want to handle events.
//
CComQIPtr<IConnectionPointContainer,
&IID_IConnectionPointContainer> spCPContainer(m_spWebBrowser2);

if (spCPContainer != NULL)
{
CComPtr<IConnectionPoint> spConnectionPoint;

hr = spCPContainer->FindConnectionPoint(DIID_DWebBrowserEvents2,
&spConnectionPoint);
if (SUCCEEDED(hr))
{
if (eConnectType == Advise)
{
//
// Advise the client site of our desire to be handle events
//
hr = spConnectionPoint->Advise((IDispatch*)this, &m_dwCookie);
if (FAILED(hr))
ATLTRACE("\n%s: ManageConnection(): Failed to Advise\n\n",
pszAppName);
}
else
{
// Remove us from the list of people interested...
hr = spConnectionPoint->Unadvise(m_dwCookie);
if (FAILED(hr))
ATLTRACE("\npszAppName: ManageConnection(): Failed to
Unadvise\n\n", pszAppName);
}
}
}

return (SUCCEEDED(hr));
}

//
// IOleObjectWithSite Methods
//
STDMETHODIMP CIEHlprObj::SetSite(IUnknown *pUnkSite)
{
USES_CONVERSION;

if (!pUnkSite)
ATLTRACE("\nSetSite(): pUnkSite is NULL\n\n");
else
{
BOOL bIsExplorer = FALSE;
// get our process id.
DWORD procID = GetCurrentProcessId();
if (procID)
{
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ, FALSE, procID );
if (hProcess)
bIsExplorer = ProcessIsInternetExplorer((PROCENUMPROC)
MyProcessEnumerator, 0, procID, hProcess);
}

if (bIsExplorer)
{
// invoke the real .NET BOH here.

//CoCreateInstance(...

}

// Query pUnkSite for the IWebBrowser2 interface.
m_spWebBrowser2 = pUnkSite;

if (m_spWebBrowser2)
{
// Create the events dialog box
//m_dlgEvents.Create:):GetDesktopWindow());
//m_dlgEvents.ShowWindow(SW_SHOWNORMAL);

// Connect to the browser in order to handle events.
if (!ManageConnection(Advise))
::MessageBox(NULL, _T("Failure sinking events from
IWebBrowser2"), (const char *)pszAppName, MB_OK);
}
}

return S_OK;
}
////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////

That's what I've got for you. I hope this is information you can use.

Thank you,
Jeff Layman
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top