586 lines
19 KiB
C++
586 lines
19 KiB
C++
// regsvr.cpp : Program to invoke OLE self-registration on a DLL.
|
|
//
|
|
// This is a part of the Microsoft Foundation Classes C++ library.
|
|
// Copyright (C) 1992-1995 Microsoft Corporation
|
|
// All rights reserved.
|
|
//
|
|
// This source code is only intended as a supplement to the
|
|
// Microsoft Foundation Classes Reference and related
|
|
// electronic documentation provided with the library.
|
|
// See these sources for detailed information regarding the
|
|
// Microsoft Foundation Classes product.
|
|
|
|
#include <windows.h>
|
|
#include <ole2.h>
|
|
#include <tchar.h>
|
|
#include <stdio.h>
|
|
#include "resource.h"
|
|
|
|
#define FAIL_ARGS 1
|
|
#define FAIL_OLE 2
|
|
#define FAIL_LOAD 3
|
|
#define FAIL_ENTRY 4
|
|
#define FAIL_REG 5
|
|
|
|
const TCHAR _szAppName[] = _T("RegSvr32");
|
|
const char _szDllInstall[] = "DllInstall";
|
|
const TCHAR _tszDllInstall[] = TEXT("DllInstall");
|
|
TCHAR _szDllPath[_MAX_PATH];
|
|
|
|
// Leave room for "Ex" to be tacked onto end
|
|
char _szDllRegSvr[32] = "DllRegisterServer";
|
|
TCHAR _tszDllRegSvr[32] = TEXT("DllRegisterServer");
|
|
char _szDllUnregSvr[32] = "DllUnregisterServer";
|
|
TCHAR _tszDllUnregSvr[32] = TEXT("DllUnregisterServer");
|
|
char _szRegContext[_MAX_PATH];
|
|
|
|
HINSTANCE _hInstance;
|
|
|
|
BOOL _bSilent;
|
|
|
|
void
|
|
FormatString3(
|
|
LPTSTR lpszOut,
|
|
LPCTSTR lpszFormat,
|
|
LPCTSTR lpsz1,
|
|
LPCTSTR lpsz2,
|
|
LPCTSTR lpsz3
|
|
)
|
|
{
|
|
LPCTSTR pchSrc = lpszFormat;
|
|
LPTSTR pchDest = lpszOut;
|
|
LPCTSTR pchTmp;
|
|
while (*pchSrc != '\0') {
|
|
if (pchSrc[0] == '%' && (pchSrc[1] >= '1' && pchSrc[1] <= '3')) {
|
|
if (pchSrc[1] == '1')
|
|
pchTmp = lpsz1;
|
|
else if (pchSrc[1] == '2')
|
|
pchTmp = lpsz2;
|
|
else
|
|
pchTmp = lpsz3;
|
|
|
|
lstrcpy(pchDest, pchTmp);
|
|
pchDest += lstrlen(pchDest);
|
|
pchSrc += 2;
|
|
} else {
|
|
if (_istlead(*pchSrc))
|
|
*pchDest++ = *pchSrc++; // copy first of 2 bytes
|
|
*pchDest++ = *pchSrc++;
|
|
}
|
|
}
|
|
*pchDest = '\0';
|
|
}
|
|
|
|
#define MAX_STRING 1024
|
|
|
|
void
|
|
DisplayMessage(
|
|
UINT ids,
|
|
LPCTSTR pszArg1 = NULL,
|
|
LPCTSTR pszArg2 = NULL,
|
|
LPCTSTR pszArg3 = NULL,
|
|
BOOL bUsage = FALSE,
|
|
BOOL bInfo = FALSE
|
|
)
|
|
{
|
|
if (_bSilent)
|
|
return;
|
|
|
|
TCHAR szFmt[MAX_STRING];
|
|
LoadString(_hInstance, ids, szFmt, MAX_STRING);
|
|
|
|
TCHAR szText[MAX_STRING];
|
|
FormatString3(szText, szFmt, pszArg1, pszArg2, pszArg3);
|
|
if (bUsage) {
|
|
int cch = _tcslen(szText);
|
|
LoadString(_hInstance, IDS_USAGE, szText + cch, MAX_STRING - cch);
|
|
}
|
|
|
|
if (! _bSilent)
|
|
MessageBox(NULL, szText, _szAppName,
|
|
MB_TASKMODAL | (bInfo ? MB_ICONINFORMATION : MB_ICONEXCLAMATION));
|
|
}
|
|
|
|
inline void
|
|
Usage(
|
|
UINT ids,
|
|
LPCTSTR pszArg1 = NULL,
|
|
LPCTSTR pszArg2 = NULL
|
|
)
|
|
{
|
|
DisplayMessage(ids, pszArg1, pszArg2, NULL, TRUE);
|
|
}
|
|
|
|
inline void
|
|
Info(
|
|
UINT ids,
|
|
LPCTSTR pszArg1 = NULL,
|
|
LPCTSTR pszArg2 = NULL
|
|
)
|
|
{
|
|
DisplayMessage(ids, pszArg1, pszArg2, NULL, FALSE, TRUE);
|
|
}
|
|
|
|
#define MAX_APPID 256
|
|
|
|
BOOL IsContextRegFileType(LPCTSTR *ppszDllName)
|
|
{
|
|
HKEY hk1, hk2;
|
|
LONG lRet;
|
|
LONG cch;
|
|
TCHAR szExt[_MAX_EXT];
|
|
TCHAR szAppID[MAX_APPID];
|
|
_tsplitpath(*ppszDllName, NULL, NULL, NULL, szExt);
|
|
|
|
// Find [HKEY_CLASSES_ROOT\.foo]
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CLASSES_ROOT, szExt, 0, KEY_QUERY_VALUE, &hk1))
|
|
return FALSE;
|
|
|
|
// Read [HKEY_CLASSES_ROOT\.foo\"foo_auto_file"]
|
|
cch = sizeof(szAppID);
|
|
lRet = RegQueryValue(hk1, NULL, szAppID, &cch);
|
|
RegCloseKey(hk1);
|
|
if (ERROR_SUCCESS != lRet)
|
|
return FALSE;
|
|
|
|
// Find [HKEY_CLASSES_ROOT\foo_auto_file]
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CLASSES_ROOT, szAppID, 0, KEY_QUERY_VALUE, &hk1))
|
|
return FALSE;
|
|
|
|
// Find [HKEY_CLASSES_ROOT\foo_auto_file\AutoRegister]
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(hk1, TEXT("AutoRegister"), 0, KEY_QUERY_VALUE, &hk2))
|
|
{
|
|
RegCloseKey(hk1);
|
|
return FALSE;
|
|
}
|
|
|
|
// Read [HKEY_CLASSES_ROOT\foo_auto_file\AutoRegister\"d:\...\fooreg.dll"]
|
|
cch = MAX_PATH;
|
|
lRet = RegQueryValue(hk2, NULL, _szDllPath, &cch);
|
|
RegCloseKey(hk1);
|
|
RegCloseKey(hk2);
|
|
if (ERROR_SUCCESS != lRet)
|
|
return FALSE;
|
|
|
|
_szDllPath[cch] = TEXT('\0');
|
|
*ppszDllName = _szDllPath;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int PASCAL
|
|
_tWinMain(
|
|
HINSTANCE hInstance,
|
|
HINSTANCE,
|
|
LPSTR,
|
|
int
|
|
)
|
|
{
|
|
int iReturn = 0;
|
|
HRESULT (STDAPICALLTYPE * lpDllEntryPointReg)(void);
|
|
HRESULT (STDAPICALLTYPE * lpDllEntryPointRegEx)(LPCSTR);
|
|
HRESULT (STDAPICALLTYPE * lpDllEntryPointRegExW)(LPCWSTR);
|
|
HRESULT (STDAPICALLTYPE * lpDllEntryPointInstall)(BOOL, LPWSTR);
|
|
HRESULT rc;
|
|
BOOL bVisualC = FALSE;
|
|
BOOL bUnregister = FALSE;
|
|
BOOL bCallDllInstall = FALSE;
|
|
BOOL bCallDllRegisterServer = TRUE;
|
|
BOOL bErrorsOnly = FALSE;
|
|
BOOL bContextReg = FALSE;
|
|
BOOL bUnicodeContextReg = FALSE;
|
|
LPSTR pszDllEntryPoint = _szDllRegSvr;
|
|
LPTSTR ptszDllEntryPoint = _tszDllRegSvr;
|
|
LPTSTR pszTok;
|
|
LPCTSTR pszDllName;
|
|
LPSTR pszContext;
|
|
LPCTSTR pszContextW;
|
|
TCHAR pszDllInstallCmdLine[MAX_PATH];
|
|
#ifdef UNICODE
|
|
PWCHAR pwszDllInstallCmdLine = pszDllInstallCmdLine;
|
|
#else
|
|
WCHAR pwszDllInstallCmdLine[MAX_PATH];
|
|
#endif
|
|
int iNumDllsToRegister = 0;
|
|
int iCount;
|
|
LPCTSTR ppszDllNames[255];
|
|
TCHAR szError[1024];
|
|
|
|
_hInstance = hInstance;
|
|
|
|
// Parse command line arguments.
|
|
int iTok;
|
|
for (iTok = 1; iTok < __argc; iTok++) {
|
|
pszTok = __targv[iTok];
|
|
|
|
if ((pszTok[0] == TEXT('-')) || (pszTok[0] == TEXT('/'))) {
|
|
switch (pszTok[1]) {
|
|
case TEXT('e'):
|
|
case TEXT('E'):
|
|
bErrorsOnly = TRUE;
|
|
break;
|
|
|
|
case TEXT('i'):
|
|
case TEXT('I'):
|
|
bCallDllInstall = TRUE;
|
|
|
|
if (pszTok[2] == TEXT(':'))
|
|
{
|
|
if (pszTok[3] == TEXT('"')) {
|
|
// handle quoted InstallCmdLine (
|
|
// (e.g. /i:"c:\my dll dir\mydll.dll")
|
|
LPTSTR pszEndQuote = &pszTok[4];
|
|
int iLength = lstrlen(pszEndQuote);
|
|
|
|
if ((iLength > 0) && pszEndQuote[iLength - 1] == TEXT('"')) {
|
|
// they quoted the string but it wasent really necessary
|
|
// (e.g. /i:"shell32.dll")
|
|
pszEndQuote[iLength - 1] = TEXT('\0');
|
|
lstrcpy(pszDllInstallCmdLine, pszEndQuote);
|
|
} else {
|
|
// we have a quoted string that spans multiple tokens
|
|
lstrcpy(pszDllInstallCmdLine, pszEndQuote);
|
|
|
|
for (iTok++; iTok < __argc; iTok++) {
|
|
// grab the next token
|
|
pszEndQuote = __targv[iTok];
|
|
iLength = lstrlen(pszEndQuote);
|
|
|
|
if ((iLength > 0) && (pszEndQuote[iLength - 1] == '"')) {
|
|
pszEndQuote[iLength - 1] = TEXT('\0');
|
|
lstrcat(pszDllInstallCmdLine, TEXT(" "));
|
|
lstrcat(pszDllInstallCmdLine, pszEndQuote);
|
|
break;
|
|
}
|
|
|
|
lstrcat(pszDllInstallCmdLine, TEXT(" "));
|
|
lstrcat(pszDllInstallCmdLine, pszEndQuote);
|
|
}
|
|
}
|
|
} else {
|
|
// cmd line is NOT quoted
|
|
lstrcpy(pszDllInstallCmdLine, &pszTok[3]);
|
|
}
|
|
#ifndef UNICODE
|
|
if (!MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
(LPCTSTR)pszDllInstallCmdLine,
|
|
-1,
|
|
pwszDllInstallCmdLine,
|
|
MAX_PATH))
|
|
{
|
|
Usage(IDS_UNRECOGNIZEDFLAG, pszTok);
|
|
return FAIL_ARGS;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
lstrcpyW((LPWSTR)pwszDllInstallCmdLine, L"");
|
|
}
|
|
break;
|
|
|
|
case TEXT('n'):
|
|
case TEXT('N'):
|
|
bCallDllRegisterServer = FALSE;
|
|
break;
|
|
|
|
case TEXT('s'):
|
|
case TEXT('S'):
|
|
_bSilent = TRUE;
|
|
break;
|
|
|
|
case TEXT('u'):
|
|
case TEXT('U'):
|
|
bUnregister = TRUE;
|
|
pszDllEntryPoint = _szDllUnregSvr;
|
|
ptszDllEntryPoint = _tszDllUnregSvr;
|
|
break;
|
|
|
|
case TEXT('v'):
|
|
case TEXT('V'):
|
|
bVisualC = TRUE;
|
|
break;
|
|
|
|
case TEXT('c'):
|
|
case TEXT('C'):
|
|
// Ignore this
|
|
break;
|
|
|
|
default:
|
|
Usage(IDS_UNRECOGNIZEDFLAG, pszTok);
|
|
return FAIL_ARGS;
|
|
}
|
|
} else {
|
|
if (pszTok[0] == TEXT('"')) {
|
|
// handle quoted DllName
|
|
TCHAR szTemp[MAX_PATH];
|
|
LPTSTR pszQuotedDllName;
|
|
int iLength;
|
|
|
|
lstrcpy(szTemp, &pszTok[1]);
|
|
iLength = lstrlen(szTemp);
|
|
|
|
if ((iLength > 0) && szTemp[iLength - 1] != TEXT('"')) {
|
|
// handle quoted dll name that spans multiple tokens
|
|
for (iTok++; iTok < __argc; iTok++) {
|
|
lstrcat(szTemp, TEXT(" "));
|
|
lstrcat(szTemp, __targv[iTok]);
|
|
iLength = lstrlen(__targv[iTok]);
|
|
if ((iLength > 0) && __targv[iTok][iLength - 1] == TEXT('"')) {
|
|
// this token has the end quote, so stop here
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
iLength = lstrlen(szTemp);
|
|
|
|
// remove the trailing " if one exists
|
|
if ( (iLength > 0) && (szTemp[iLength - 1] == TEXT('"')) ) {
|
|
szTemp[iLength - 1] = TEXT('\0');
|
|
}
|
|
|
|
pszQuotedDllName = (LPTSTR) LocalAlloc(LPTR, (iLength + 1) * sizeof(TCHAR));
|
|
|
|
if (pszQuotedDllName)
|
|
{
|
|
lstrcpy(pszQuotedDllName, szTemp);
|
|
ppszDllNames[iNumDllsToRegister] = pszQuotedDllName;
|
|
iNumDllsToRegister++;
|
|
}
|
|
|
|
} else {
|
|
// no leading " so assume that this token is one of the dll names
|
|
ppszDllNames[iNumDllsToRegister] = pszTok;
|
|
iNumDllsToRegister++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check to see if we were passed a '-n' but no '-i'
|
|
if (!bCallDllRegisterServer && !bCallDllInstall) {
|
|
Usage(IDS_UNRECOGNIZEDFLAG, TEXT("/n must be used with the /i switch"));
|
|
return FAIL_ARGS;
|
|
}
|
|
|
|
if (iNumDllsToRegister == 0) {
|
|
if (bVisualC)
|
|
DisplayMessage(IDS_NOPROJECT);
|
|
else
|
|
Usage(IDS_NODLLNAME);
|
|
|
|
return FAIL_ARGS;
|
|
}
|
|
|
|
// Initialize OLE.
|
|
__try {
|
|
rc = OleInitialize(NULL);
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
rc = (HRESULT) GetExceptionCode();
|
|
}
|
|
|
|
if (FAILED(rc)) {
|
|
DisplayMessage(IDS_OLEINITFAILED);
|
|
return FAIL_OLE;
|
|
}
|
|
|
|
if (_bSilent) {
|
|
SetErrorMode(SEM_FAILCRITICALERRORS); // Make sure LoadLib fail in silent mode (no popups).
|
|
}
|
|
|
|
for (iCount = 0; iCount < iNumDllsToRegister; iCount++) {
|
|
pszDllName = ppszDllNames[iCount];
|
|
|
|
/*
|
|
* See if this is a non-executable file that requires special handling. If so,
|
|
* bContextReg will be set to TRUE and pszDllName (which original pointed to
|
|
* the path to the special file) will be set to the path to the executable that
|
|
* is responsible for doing the actual registration. The path to the special
|
|
* file will be passed in as context info in the call Dll[Un]RegisterServerEx.
|
|
*/
|
|
pszContextW = pszDllName;
|
|
pszContext = (LPSTR)pszContextW;
|
|
bContextReg = IsContextRegFileType(&pszDllName);
|
|
if (TRUE == bContextReg) {
|
|
lstrcatA(pszDllEntryPoint, "Ex");
|
|
lstrcat(ptszDllEntryPoint, TEXT("Ex"));
|
|
// Convert pszContext to a real char *
|
|
#ifdef UNICODE
|
|
if (!WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
(LPCWSTR)pszContext,
|
|
lstrlenW((LPCWSTR)pszContext),
|
|
_szRegContext,
|
|
sizeof(_szRegContext),
|
|
0,
|
|
NULL))
|
|
{
|
|
Usage(IDS_UNRECOGNIZEDFLAG, pszTok);
|
|
return FAIL_ARGS;
|
|
} else {
|
|
pszContext = _szRegContext;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
// Load the library -- fail silently if problems
|
|
UINT errMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
HINSTANCE hLib = LoadLibraryEx(pszDllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
|
|
SetErrorMode(errMode);
|
|
|
|
if (hLib < (HINSTANCE)HINSTANCE_ERROR) {
|
|
DWORD dwErr = GetLastError();
|
|
|
|
if (ERROR_BAD_EXE_FORMAT == dwErr) {
|
|
DisplayMessage(IDS_NOTEXEORHELPER, pszDllName);
|
|
} else {
|
|
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL,
|
|
dwErr, 0, szError, sizeof(szError), NULL)) {
|
|
DisplayMessage(IDS_LOADLIBFAILED, pszDllName, szError);
|
|
}
|
|
}
|
|
iReturn = FAIL_LOAD;
|
|
goto CleanupOle;
|
|
}
|
|
|
|
// during unregister we need to call DllInstall first, and then DllUnregisterServer
|
|
if (bUnregister)
|
|
goto DllInstall;
|
|
|
|
DllRegisterServer:
|
|
// Call the entry point for DllRegisterServer/DllUnregisterServer
|
|
if (bCallDllRegisterServer) {
|
|
if (bContextReg) {
|
|
(FARPROC&)lpDllEntryPointRegEx = GetProcAddress(hLib, "DllRegisterServerExW");
|
|
if (lpDllEntryPointRegEx) {
|
|
(FARPROC&)lpDllEntryPointRegExW = (FARPROC&)lpDllEntryPointRegEx;
|
|
bUnicodeContextReg = TRUE;
|
|
} else {
|
|
(FARPROC&)lpDllEntryPointRegEx = GetProcAddress(hLib, "DllRegisterServerEx");
|
|
}
|
|
|
|
(FARPROC&)lpDllEntryPointReg = (FARPROC&)lpDllEntryPointRegEx;
|
|
} else {
|
|
(FARPROC&)lpDllEntryPointReg = (FARPROC&)lpDllEntryPointRegEx = GetProcAddress(hLib, pszDllEntryPoint);
|
|
}
|
|
|
|
if (lpDllEntryPointReg == NULL) {
|
|
TCHAR szExt[_MAX_EXT];
|
|
_tsplitpath(pszDllName, NULL, NULL, NULL, szExt);
|
|
|
|
if (FALSE == bContextReg && (lstrcmp(szExt, TEXT(".dll")) != 0) && (lstrcmp(szExt, TEXT(".ocx")) != 0))
|
|
DisplayMessage(IDS_NOTDLLOROCX, pszDllName, ptszDllEntryPoint);
|
|
else
|
|
DisplayMessage(IDS_NOENTRYPOINT, pszDllName, ptszDllEntryPoint);
|
|
|
|
iReturn = FAIL_ENTRY;
|
|
goto CleanupLibrary;
|
|
}
|
|
|
|
// try calling DllRegisterServer[Ex]() / DllUnregisterServer[Ex]()
|
|
__try {
|
|
if (bUnicodeContextReg) {
|
|
rc = (*lpDllEntryPointRegExW)(pszContextW);
|
|
} else {
|
|
if (bContextReg) {
|
|
rc = (*lpDllEntryPointRegEx)(pszContext);
|
|
} else {
|
|
rc = (*lpDllEntryPointReg)();
|
|
}
|
|
}
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
rc = (HRESULT) GetExceptionCode();
|
|
}
|
|
|
|
if (FAILED(rc)) {
|
|
wsprintf(szError, _T("0x%08lx"), rc);
|
|
DisplayMessage(IDS_CALLFAILED, ptszDllEntryPoint, pszDllName, szError);
|
|
iReturn = FAIL_REG;
|
|
goto CleanupLibrary;
|
|
}
|
|
}
|
|
|
|
// during unregister we need to call DllInstall first, then DllRegisterServer,
|
|
// since we already called DllInstall and then jumped back up to DllRegisterServer:
|
|
// skip over it and goto CheckErrors:
|
|
if (bUnregister)
|
|
goto CheckErrors;
|
|
|
|
DllInstall:
|
|
// Call the entry point for DllInstall
|
|
if (bCallDllInstall) {
|
|
(FARPROC&)lpDllEntryPointInstall = GetProcAddress(hLib, _szDllInstall);
|
|
|
|
if (lpDllEntryPointInstall == NULL) {
|
|
TCHAR szExt[_MAX_EXT];
|
|
_tsplitpath(pszDllName, NULL, NULL, NULL, szExt);
|
|
|
|
if ((lstrcmp(szExt, TEXT(".dll")) != 0) && (lstrcmp(szExt, TEXT(".ocx")) != 0))
|
|
DisplayMessage(IDS_NOTDLLOROCX, pszDllName, _tszDllInstall);
|
|
else
|
|
DisplayMessage(IDS_NOENTRYPOINT, pszDllName, _tszDllInstall);
|
|
|
|
iReturn = FAIL_ENTRY;
|
|
goto CleanupLibrary;
|
|
}
|
|
|
|
// try calling DllInstall(BOOL bRegister, LPWSTR lpwszCmdLine) here...
|
|
// NOTE: the lpwszCmdLine string must be UNICODE!
|
|
__try {
|
|
rc = (*lpDllEntryPointInstall)(!bUnregister, pwszDllInstallCmdLine);
|
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
rc = (HRESULT) GetExceptionCode();
|
|
}
|
|
|
|
if (FAILED(rc)) {
|
|
wsprintf(szError, _T("0x%08lx"), rc);
|
|
DisplayMessage(IDS_CALLFAILED, _tszDllInstall, pszDllName, szError);
|
|
iReturn = FAIL_REG;
|
|
goto CleanupLibrary;
|
|
}
|
|
}
|
|
|
|
// during unregister we now need to call DllUnregisterServer
|
|
if (bUnregister)
|
|
goto DllRegisterServer;
|
|
|
|
CheckErrors:
|
|
if (!bErrorsOnly) {
|
|
TCHAR szMessage[MAX_PATH];
|
|
|
|
// set up the success message text
|
|
if (bCallDllRegisterServer)
|
|
{
|
|
lstrcpy(szMessage, ptszDllEntryPoint);
|
|
if (bCallDllInstall)
|
|
{
|
|
lstrcat(szMessage, TEXT(" and "));
|
|
lstrcat(szMessage, _tszDllInstall);
|
|
}
|
|
}
|
|
else if (bCallDllInstall)
|
|
{
|
|
lstrcpy(szMessage, _tszDllInstall);
|
|
}
|
|
|
|
Info(IDS_CALLSUCCEEDED, szMessage, pszDllName);
|
|
}
|
|
|
|
CleanupLibrary:
|
|
FreeLibrary(hLib);
|
|
}
|
|
|
|
CleanupOle:
|
|
__try {
|
|
OleUninitialize();
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
DisplayMessage(IDS_OLEUNINITFAILED);
|
|
}
|
|
|
|
return iReturn;
|
|
}
|