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.