JVIEW.CPP

/* 
* jview.cpp - JView front end, including command-line parsing logic.
*
* (C) Copyright 1996, Microsoft Corporation and it suppliers.
*/

#pragma hdrstop

#include "jview.h"
#include "javaprop.hpp"

// Macros

#define WIN32_ERROR_TO_HRESULT(err) MAKE_SCODE(SEVERITY_ERROR, FACILITY_WIN32, (err))

#define WIN32_RESULT_TO_HRESULT(err) ((err) == ERROR_SUCCESS ? S_OK : WIN32_ERROR_TO_HRESULT(err))

#define LAST_WIN32_ERROR_TO_HRESULT() WIN32_RESULT_TO_HRESULT(GetLastError())

#define APPLETVIEWER "sun.applet.AppletViewer"

//------------------------------------------------------------------------------
// LoadOemString
//
// Wrapper for the Win32 LoadString API that translates the string to the OEM
// character set before returning it to the caller.
//------------------------------------------------------------------------------

BOOL LoadOemString(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int
nBufferMax)
{
BOOL fResult;

fResult = LoadString(hInstance, uID, lpBuffer, nBufferMax);

if (fResult)
CharToOem(lpBuffer, lpBuffer);

return fResult;
}

//------------------------------------------------------------------------------
// CJView::CJView:
// Constructor
//------------------------------------------------------------------------------
CJView::CJView (int ac, char **av) : m_ac (ac), m_av (av)
{
m_fApplet = FALSE;
m_fPause = FALSE;
m_fVerify = FALSE;
m_pszClassPath = NULL;
m_pszAppend = NULL;
m_pszPrepend = NULL;
m_pszClassName = NULL;
m_ppszArgs = NULL;
m_iArgs = 0;
m_pJE = NULL;
m_pProperties = NULL;
}

//------------------------------------------------------------------------------
// CJView::~CJView:
// Destructor
//------------------------------------------------------------------------------
CJView::~CJView ()
{
deleteSZ(m_pszClassPath);
deleteSZ(m_pszClassName);
deleteSZ(m_pszAppend);
deleteSZ(m_pszPrepend);

if (m_ppszArgs)
{
INT n = 0;

while (m_ppszArgs[n] != NULL)
delete [] m_ppszArgs[n++];
delete [] m_ppszArgs;
}

if (m_pProperties)
{
m_pProperties->Release();
delete(m_pProperties);
}

if (m_pJE)
{
m_pJE->Release();
CoUninitialize();
}
}

//------------------------------------------------------------------------------
// CJView::m_Pause:
//
// -p was given on the command line, and we have an error, thus we display
// amessage to the user to press a key to terminate JView, thus allowing enough
// time to read the message before JView termnates and the console it was
// running in goes away if being executed from the IDE
//
// Returns: Nothing
//------------------------------------------------------------------------------
void CJView::m_Pause()
{
CHAR szText[BUFSIZE];

LoadOemString(NULL, IDS_PRESSANYKEY, szText, sizeof(szText));
fprintf(stderr, "%s", szText);
_getch();
}

//------------------------------------------------------------------------------
// CJView::m_FatalError:
//
// Print a formatted error message to stderr
//
// Returns: Nothing
//------------------------------------------------------------------------------
void CJView::m_FatalError
(
INT idString,
...
)
{
CHAR szFmt[BUFSIZE];
va_list va;
va_start(va, idString);


LoadOemString(NULL, IDS_ERROR, szFmt, sizeof(szFmt));
fprintf(stderr, szFmt);

if (idString)
LoadOemString(NULL, idString, szFmt, sizeof(szFmt));
else
lstrcpy(szFmt, "%s");

vfprintf(stderr, szFmt, va);
va_end(va);
fprintf(stderr, "\n");

if (m_fPause)
m_Pause();
}

//------------------------------------------------------------------------------
// CJView::m_FatalErrorHR:
//
// Print a formatted error followup by an hresult tp stderr
//------------------------------------------------------------------------------
void CJView::m_FatalErrorHR
(
HRESULT hr,
INT idString,
...
)
{
CHAR szFmt[BUFSIZE];
CHAR buf[BUFSIZE];
DWORD res;
va_list va;
va_start(va, idString);

LoadOemString(NULL, IDS_ERROR, szFmt, sizeof(szFmt));
fprintf(stderr, szFmt);
LoadOemString(NULL, idString, szFmt, sizeof(szFmt));
vfprintf(stderr, szFmt, va);
va_end(va);

res = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
(DWORD)hr,
LOCALE_SYSTEM_DEFAULT,
buf,
sizeof(buf),
NULL);

CHAR szNoMain[BUFSIZE] = "";

if (!res)
{
CHAR szSCODE[BUFSIZE];

LoadOemString(NULL, IDS_SCODE, szSCODE, sizeof(szSCODE));
sprintf(buf, szSCODE, (DWORD)hr);
}
else
{
// Convert ANSI string returned by FormatMessage to OEM.
CharToOem(buf, buf);

// Now we check if the error is "Member not found", and if it is, we
// will append some additional info to the error message letting
// the user know it was main() that could not be found, since that
// is the only time this message should be generated.
if (hr == DISP_E_MEMBERNOTFOUND)
{
CHAR sz[BUFSIZE] = "";

LoadOemString(NULL, IDS_NOMAIN, sz, sizeof(sz));
sprintf(szNoMain, sz, m_pszClassName, m_pszClassName);
}
else
*szNoMain = '\0';
}

fprintf(stderr, ": %s\n", buf);
if (*szNoMain)
fprintf(stderr, "%s", szNoMain);

if (m_fPause)
m_Pause();
}

//------------------------------------------------------------------------------
// CJView::m_InitComAndJava:
//
// Initializes COM and obtains the neccessary interfaces from the Java VM
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::m_InitComAndJava ()
{
HRESULT hr = E_UNEXPECTED;
IClassFactory *pcf = NULL;

hr = CoInitialize(NULL);

if (FAILED(hr))
{
m_FatalErrorHR(hr, IDS_COULDNOTINITOLE);
}
else
{
hr = CoGetClassObject(CLSID_JavaExecute,
CLSCTX_INPROC_SERVER |
CLSCTX_INPROC_HANDLER |
CLSCTX_LOCAL_SERVER,
NULL,
IID_IClassFactory,
(LPVOID*)(&pcf));
if (FAILED(hr))
{
m_FatalErrorHR(hr, IDS_JAVAVM);
}
else
{
hr = pcf->CreateInstance(NULL, IID_IJavaExecute, (LPVOID *)(&m_pJE));
if (FAILED(hr))
{
m_pJE = NULL;
m_FatalErrorHR(hr, IDS_CLASSLOADER);
}

pcf->Release();
}

if (NULL == m_pJE)
CoUninitialize();
}

return (m_pJE != NULL);
}

//------------------------------------------------------------------------------
// CJView::MB2WC:
//
// Converts the multibyte string to a UNICODE string, allocating space
// for the destination string.
//
// Returns: Pointer to newly allocated and converted string, NULL if it fails
//------------------------------------------------------------------------------
LPWSTR CJView::m_MB2WC
(
LPCSTR szAnsi,
int cchAnsi
)
{
// First, determine size of converted string
//--------------------------------------------------------------------------
LPWSTR pwsz = NULL;
int cchWide = MultiByteToWideChar(0, 0, szAnsi, cchAnsi, NULL, 0) + 1;

if (cchWide > 0)
{
// Got size so allocate the space and convert the string
//----------------------------------------------------------------------
if (pwsz = new WCHAR[cchWide])
MultiByteToWideChar(0, 0, szAnsi, cchAnsi, pwsz, cchWide);
}

return pwsz;
}

//------------------------------------------------------------------------------
// CJView::m_WC2MB:
//
// Converts the given UNICODE string to a multibyte string, allocating space
// for the destination string.
//
// Returns: Pointer to newly allocated and converted string, NULL if it fails
//------------------------------------------------------------------------------
LPSTR CJView::m_WC2MB
(
LPCWSTR pwsz,
int cchWide
)
{
// First, determine size of converted string
//--------------------------------------------------------------------------
LPSTR psz = NULL;
int cchAnsi = WideCharToMultiByte(0, 0, pwsz, cchWide, NULL, 0, NULL, NULL);

if (cchAnsi > 0)
{
// Got size so allocate the space and convert the string
//----------------------------------------------------------------------
if (psz = new CHAR[cchAnsi])
WideCharToMultiByte(0, 0, pwsz, cchWide, psz, cchAnsi, NULL, NULL);
}

return psz;
}


//------------------------------------------------------------------------------
// CJView::m_newSZ:
//
// Allocates the given string, generating OUT OF MEMORY if it fails
//
// Returns: LPSTR to allocated buffer if successful, NULL if not
//------------------------------------------------------------------------------
LPSTR CJView::m_newSZ
(
int cBytes
)
{
LPSTR psz = new CHAR[cBytes + 1]; // +1 for \0

if (!psz)
m_FatalError(IDS_OUTOFMEMORY);

return psz;
}

//------------------------------------------------------------------------------
// CJView::m_AppendPathString
//
// Appends the given path the the other given path, allocating and freeing
// as neccessary
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::m_AppendPathString
(
LPSTR *ppszPath,
LPSTR pszAppend
)
{
LPSTR psz = NULL;
BOOL fSemi2 = *CharPrev(pszAppend, pszAppend + lstrlen(pszAppend)) != ';';

if (*ppszPath)
{
// Been here before, so we append the given string to the given path
//----------------------------------------------------------------------
if (!(psz = m_newSZ(lstrlen(*ppszPath))))
return FALSE;

lstrcpy(psz, *ppszPath);
deleteSZ(*ppszPath);

BOOL fSemi1 = *CharPrev(psz, psz + lstrlen(psz)) != ';';

if (!(*ppszPath = m_newSZ(lstrlen(psz) + lstrlen(pszAppend) + fSemi1 + fSemi2)))
{
deleteSZ(psz);
return FALSE;
}

lstrcpy(*ppszPath, psz);

// Add semi-colon between paths if original path did not have one
//----------------------------------------------------------------------
if (fSemi1)
lstrcat(*ppszPath, ";");

lstrcat(*ppszPath, pszAppend);
deleteSZ(psz);
}
else
{
// First time here, so copy Append string into Path
//----------------------------------------------------------------------
if (!(*ppszPath = m_newSZ(lstrlen(pszAppend) + fSemi2)))
return FALSE;

lstrcpy(*ppszPath , pszAppend);
}

// Append final semi-colon if string being append does not have one
//--------------------------------------------------------------------------
if (fSemi2)
lstrcat(*ppszPath, ";");

return TRUE;
}

//------------------------------------------------------------------------------
// CJView::m_DisplayUsage:
//
// Displays the usage text for JView.exe
//
// Returns: Nothing
//------------------------------------------------------------------------------
VOID CJView::m_DisplayUsage ()
{
// NOTE: All usage lines must be sequential String
// IDS starting with IDS_USAGE1
CHAR sz[BUFSIZE];
INT i = 0;

m_DisplayBanner();

while (TRUE)
{
LoadOemString(NULL, IDS_USAGE1 + (i++), sz, sizeof(sz));
if (*sz == '~')
break;
printf(sz);
}
}

//------------------------------------------------------------------------------
// CJView::m_ParseSwitches:
//
// Parses off the switches portion of the command line
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::m_ParseSwitches
(
int *piArg
)
{
BOOL fSuccess = TRUE;
BOOL fReplace = m_pszClassPath != NULL;
LPSTR *ppsz;
LPSTR psz;

*piArg = 1;

// We support both '-' and '/' switch designators
//--------------------------------------------------------------------------
while ((*piArg < m_ac) && ((m_av[*piArg][0] == '-') || (m_av[*piArg][0] == '/')))
{
psz = CharNext(m_av[*piArg]);

// Check for the -D switch prior to CharLowering the switch string.

if ( (*psz == 'D' || *psz == 'd') && (*(psz+1)==':') )
{
char *pszPropStr = psz+2;

if ( *pszPropStr == '\0' )
{
m_FatalError(IDS_INVALIDSWITCH, psz);
fSuccess = FALSE;
break;
}

// If we don't already have a properties object, create one.
if ( !m_pProperties )
{
m_pProperties = new(CEnumJAVAPROPERTY);

if (!m_pProperties)
{
m_FatalError(IDS_OUTOFMEMORY);
fSuccess = FALSE;
break;
}
}

// Add the property to the property enumerator.
m_pProperties->Add(psz+2);
}
else
{
// All other switches are case insensitive, so just knock 'em down to
// lowercase here.

CharLower(psz);

// Check for usage switch first. This is only valid as first switch
//----------------------------------------------------------------------
if (*piArg == 1 && lstrcmp(psz, "?") == 0)
{
m_DisplayUsage();
fSuccess = FALSE;
break;
}

// Check for "run in sun.applet.AppletViewer switch" , -a
if (lstrcmp(psz, "a") == 0)
m_fApplet = TRUE;
// Check for "pause after error switch" , -p
else if (lstrcmp(psz, "p") == 0)
m_fPause = TRUE;
// Check for "verify all" switch, -v
else if (lstrcmp(psz, "v") == 0)
m_fVerify = TRUE;
// Silently ignore the old "quiet" switch for compatibility with
// existing scripts.
else if (lstrcmp(psz, "q") == 0)
;
else
{
// Check for classpath switches -cp, -cp:p, or -cp:a
//------------------------------------------------------------------
BOOL fAppend = FALSE;
BOOL fPrepend = FALSE;

if ( !lstrcmp(psz, "cp") ||
(fPrepend = !lstrcmp(psz, "cp:p")) ||
(fAppend = !lstrcmp(psz, "cp:a")))
{
// We have classpath switch, so check for path. If not given,
// i.e, we're out of arguments, it's a bogus command line
//--------------------------------------------------------------
if (++(*piArg) == m_ac ||
(m_av[*piArg][0] == '-' || m_av[*piArg][0] == '/'))
{
// We're about to exit, so we don't have to worry about
// losing information in the ANSI to OEM conversion.
CharToOem(m_av[*piArg - 1], m_av[*piArg - 1]);

m_FatalError(IDS_NOPATHGIVEN, m_av[*piArg - 1]);
fSuccess = FALSE;
break;
}

// If we are given a class path on the command line and we also
// have a classpath from the environment variable CLASSPATH,
// the one given on the command line instructs us to ignore the
// environment variable, and we instead append/prepend other
// paths to this one instead
//--------------------------------------------------------------
if (fReplace && !(fPrepend || fAppend))
{
deleteSZ(m_pszClassPath);
fReplace = FALSE;
}

ppsz = fPrepend ? &m_pszPrepend : (fAppend ? &m_pszAppend :
&m_pszClassPath);
m_AppendPathString(ppsz, m_av[*piArg]);
}
else
{
// We're about to exit, so we don't have to worry about
// losing information in the ANSI to OEM conversion.
CharToOem(m_av[*piArg], m_av[*piArg]);

// Bogus switch!
m_FatalError(IDS_INVALIDSWITCH, m_av[*piArg]);
fSuccess = FALSE;
break;
}
}
}

(*piArg)++;
}

return fSuccess;
}

//------------------------------------------------------------------------------
// CJView::m_ParseParameters
//
// Parses off the remaining command line arguments following the class simply
// copying them into a list of OLESTRS
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::m_ParseParameters
(
int iNext
)
{
// If applet or stand-alone, we need a values array
//--------------------------------------------------------------------------
m_iArgs = m_ac - iNext;

m_ppszArgs = new LPOLESTR[m_iArgs + 1];
if (!m_ppszArgs)
{
m_FatalError(IDS_OUTOFMEMORY);
return FALSE;
}

(m_ppszArgs)[0] = NULL; // Initially empty!

// Now, run through the list of arguments and process
//--------------------------------------------------------------------------
int i;

for (i = 0; i < m_iArgs; i++)
{
if (!((m_ppszArgs)[i] = m_MB2WC(m_av[iNext++])))
break;
}

// If succesful, mark end of array
//--------------------------------------------------------------------------
if (i == m_iArgs)
{
(m_ppszArgs)[i] = NULL;
}
else
{
// Clean up if we fail
//----------------------------------------------------------------------
int n;

for (n = 0; n < i; n++)
deleteSZ(m_ppszArgs[n]);
deleteSZ(m_ppszArgs);
}

return (i == m_iArgs);
}

//------------------------------------------------------------------------------
// CJView::ParseCommandLine:
//
// Parses the command line, which must be in the format:
//
// JView [switches] ClassName [Parameters]
//
// switches: None : Invoke main()
// -a : Run as Applet
// -cp <path> : Set CLASSPATH to <path.
// -cp:p <path> : Prepend <path> to CLASSPATH
// -cp:a <path> : Append <path> to CLASSPATH
//
// Classname: Class file to run. Valid previously compiled .JAVA file(s)
//
// Parameters: Name=Value
//
// if value contains spaces, value must be contained in double
// quotes
//
// Name="Value contains spaces"
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::ParseCommandLine ()
{
// Must have at least one command line arguement of class name
// Argument 0 is JVIEW itself
//--------------------------------------------------------------------------
if (m_ac == 1)
{
m_DisplayUsage();
return FALSE;
}

// Get current CLASSPATH from VM. We start with this, and append, prepend,
// or replace it with what we find on the command line
//--------------------------------------------------------------------------
LPOLESTR psz = NULL;

if (m_pszClassPath = new CHAR[1])
*m_pszClassPath = 0;

// First thing we check for are switches, processing all arguments that
// begin with "-".
// NOTE: All switches must come before class name and parameters
//--------------------------------------------------------------------------
INT i;

if (!m_ParseSwitches(&i))
return FALSE;

// Next on the command line should be the class file to execute. If no more
// arguments, it's a bogus command line
//--------------------------------------------------------------------------
BOOL fSuccess = TRUE;

if (i == m_ac)
{
m_FatalError(IDS_NOCLASSGIVEN);
fSuccess = FALSE;
}
else
{
// OK, we have another argument, so whatever it is we simply treat it as
// the class file name and let the VM deal with verifying it.
//----------------------------------------------------------------------
if (m_pszClassName = m_newSZ(lstrlen(m_fApplet ? APPLETVIEWER : m_av[i])))
{
lstrcpy(m_pszClassName, m_fApplet ? APPLETVIEWER : m_av[i++]);

// Finally, if we have any more arguments, they are all treated as
// parameters to be used by the class
//------------------------------------------------------------------
if (i < m_ac)
fSuccess = m_ParseParameters(i);
}
}

if (fSuccess)
{
// Build final path string
//----------------------------------------------------------------------
if (m_pszPrepend)
{
if (m_pszClassPath)
m_AppendPathString(&m_pszPrepend, m_pszClassPath);
deleteSZ(m_pszClassPath);
m_pszClassPath = m_pszPrepend;
m_pszPrepend = NULL;
}

if (m_pszAppend)
{
if (m_pszClassPath)
{
m_AppendPathString(&m_pszClassPath, m_pszAppend);
deleteSZ(m_pszAppend);
}
else
{
m_pszClassPath = m_pszAppend;
m_pszAppend = NULL;
}
}
}

return fSuccess;
}

//------------------------------------------------------------------------------
// CJView::m_DisplayBanner:
//
// Displays the banner for JView.exe
//
// Returns: Nothing
//------------------------------------------------------------------------------
VOID CJView::m_DisplayBanner ()
{
// Get FileVersion info
//--------------------------------------------------------------------------
DWORD dwVer = 0;

CHAR szFile[MAX_PATH];

GetModuleFileName(NULL, szFile, sizeof(szFile));

VS_FIXEDFILEINFO *lpInfo;
DWORD dw;
DWORD dwSize = GetFileVersionInfoSize(szFile, &dw);
LPVOID lpData = (LPVOID)new CHAR[dwSize];

GetFileVersionInfo(szFile, 0, dwSize, lpData);

// Get File Version and File Description
//--------------------------------------------------------------------------
UINT ui;
VerQueryValue (lpData, "\\", (LPVOID*)&lpInfo, &ui);

CHAR sz[BUFSIZE];

LoadOemString(NULL, IDS_BANNER1, sz, sizeof(sz));
printf(sz, HIWORD(lpInfo->dwFileVersionMS),
LOWORD(lpInfo->dwFileVersionMS),
LOWORD(lpInfo->dwFileVersionLS));
LoadOemString(NULL, IDS_BANNER2, sz, sizeof(sz));
printf(sz);

delete lpData;
}

//------------------------------------------------------------------------------
// CJView::Initialize:
//
// Performs initialization for CJView
//
// Returns: TRUE if successful, FALSE if not
//------------------------------------------------------------------------------
BOOL CJView::Initialize ()
{
return m_InitComAndJava();
}

//------------------------------------------------------------------------------
// RunMessageLoop:
// Message pump for OLE
//
//------------------------------------------------------------------------------
UINT RunMessageLoop(void)
{
MSG msg;

// No accelerators to load. Get and dispatch messages until a WM_QUIT
// message is received.
ZeroMemory(&msg, sizeof(msg));

msg.wParam = S_OK;

while (GetMessage(&msg, NULL, 0, 0))
//
// Dispatch message to target window.
//
// We don't have any windows, so there are no window procedures that
// require TranslateMessage(&msg).
//
DispatchMessage(&msg);

return(msg.wParam);
}

//------------------------------------------------------------------------------
// CJView::SetSystemProperties:
//
// Will inform the VM of any additional system properties that we want to
// set.

HRESULT CJView::SetSystemProperties()
{
HRESULT hr;
IJavaExecute2 *pije2;

// If no properties defined, bail.
if ( !m_pProperties )
return S_OK;

// Query for the IJavaExecute2 interface off of the existing
// IJavaExecute interface.
if (m_pJE->QueryInterface(IID_IJavaExecute2, (LPVOID*)&pije2) == S_OK)
{
// We found IJavaExecute2, set the system properties.
hr = pije2->SetSystemProperties( (IEnumJAVAPROPERTY *)m_pProperties);
pije2->Release();
}
else
{
// If the QI failed, this current VM does not support IJavaExecute2
hr = E_FAIL;
m_FatalError(IDS_PROPSNOTSUPPORTED);
}

return hr;
}

//------------------------------------------------------------------------------
// CJView::ExecuteClass:
//
// Executes the given class file
//
// Returns: result of IJavaExecute::Execute
//------------------------------------------------------------------------------

HRESULT CJView::ExecuteClass (LPERRORINFO *ppIErrorInfo)
{
LPOLESTR pszClassName;
LPOLESTR pszClassPath;
JAVAEXECUTEINFO jei;

pszClassName = m_MB2WC(m_pszClassName);
pszClassPath = m_MB2WC(m_pszClassPath);

jei.cbSize = sizeof(jei);
jei.dwFlags = m_fVerify ? JEIF_VERIFYCLASSES : 0;
jei.pszClassName = pszClassName;
jei.rgszArgs = (LPCOLESTR *)(m_ppszArgs);
jei.cArgs = m_iArgs;
jei.pszClassPath = pszClassPath;

HRESULT hr = m_pJE->Execute(&jei, ppIErrorInfo);

deleteSZ(pszClassPath);
deleteSZ(pszClassName);
return hr;
}


//------------------------------------------------------------------------------
// RunClassThread
//
// Executes the given class file. If not found, executes without .class
// extension, if it has it.
//
// Returns: 0 if successful, 1 if not
//------------------------------------------------------------------------------

DWORD _stdcall RunClassThread
(
PVOID pv
)
{
CJView* pJV = (CJView*)pv;
int iResult = 1; // Assume failure.

if (pJV->ParseCommandLine())
{
HRESULT hr;
LPERRORINFO pIErrorInfo = NULL;

// Check for prepended path

int len = strlen(pJV->m_pszClassName);

if (len > 2 && (pJV->m_pszClassName[1] == ':' || strchr(pJV->m_pszClassName,'\\') != NULL))
{
char *fullname = pJV->m_newSZ(MAX_PATH);
char *endofpath;

if (GetFullPathName(pJV->m_pszClassName, MAX_PATH+1, fullname, &endofpath))
{
// Convert to long filename, if needed

WIN32_FIND_DATA findinfo;
HANDLE hfind;

hfind = FindFirstFile(fullname, &findinfo);

if (hfind == INVALID_HANDLE_VALUE)
{
// Try appending a .class extension

strcat(endofpath, ".class");

hfind = FindFirstFile(fullname, &findinfo);
}

if (hfind != INVALID_HANDLE_VALUE)
{
// Split path from name

*(endofpath-1) = '\0';

// Create the real class name and update length


deleteSZ(pJV->m_pszClassName);
len = lstrlen(findinfo.cFileName);
pJV->m_pszClassName = pJV->m_newSZ(len);
lstrcpy(pJV->m_pszClassName, findinfo.cFileName);

// Prepend to specified path

if (*pJV->m_pszClassPath == '\0')
{
deleteSZ(pJV->m_pszClassPath);
pJV->m_pszClassPath = fullname;
}
else
{
pJV->m_AppendPathString(&fullname, pJV->m_pszClassPath);
deleteSZ(pJV->m_pszClassPath);
pJV->m_pszClassPath = fullname;
}

FindClose(hfind);
}
}

// else let the vm deal with whatever was specified.
}

// Check for .class extension.

const char classext1[] = ".cla";
const int classext1len = sizeof(classext1)-1;
const char classext2[] = ".class";
const int classext2len = sizeof(classext2)-1;

char *classnameext = NULL;

if (len > classext1len)
{
char *ext = pJV->m_pszClassName+len-classext1len;

if (_stricmp(ext, classext1) == 0)
{
// Truncate at extension.

*ext = '\0';
classnameext = ext;
}
else
{
// Try the full extension.

ext -= classext2len-classext1len;

if (_stricmp(ext, classext2) == 0)
{
*ext = '\0';
classnameext = ext;
}
}
}

// Set any user defined system properties.
hr = pJV->SetSystemProperties();

if ( hr == S_OK )
{
hr = pJV->ExecuteClass(&pIErrorInfo);

if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) && classnameext != NULL)
{
// Try the 1 in a billion possibility that someone is trying
// to execute some class named 'class' in a package. Of course,
// if someone has a class with the same name as the package, we
// hit it first above.

*classnameext = '.';

hr = pJV->ExecuteClass(&pIErrorInfo);
}

if (!SUCCEEDED(hr))
{
// We're about to exit, so we don't have to worry about
// losing information in the ANSI to OEM conversion.
CharToOem(pJV->m_pszClassName, pJV->m_pszClassName);

// Most likely .class file did not exist
pJV->m_FatalErrorHR (hr, IDS_EXECUTINGCLASS, pJV->m_pszClassName);
iResult = 1;
}
else if (pIErrorInfo)
{
// VM threw an exception while running the .class file. We
// get the info via the returned IErrorInfo interface
BSTR bstrError = NULL;

if (SUCCEEDED(pIErrorInfo->GetDescription(&bstrError)))
{
LPSTR pszError = pJV->m_WC2MB(bstrError);

if (pszError)
{
CharToOem(pszError, pszError);
pJV->m_FatalError (0, pszError);
deleteSZ(pszError);
}
else
pJV->m_FatalError (IDS_UNKNOWNERROR);

SysFreeString(bstrError);
}
else
pJV->m_FatalError(IDS_UNKNOWNERROR);

iResult = 1;

pIErrorInfo->Release();
}
else
// Success.
iResult = 0;
}
}

// Terminate message pump
PostThreadMessage(pJV->m_dwMsgLoopThreadID, WM_QUIT, 0, 0);

return (DWORD)iResult;
}

//------------------------------------------------------------------------------
// main() - Entry point for JView
//
// Returns: 0 if successful, 1 if not
//------------------------------------------------------------------------------

#ifdef NO_CONSOLE
#define ENTRYPOINT int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
#else
#define ENTRYPOINT int __cdecl main( int, char ** )
#endif

ENTRYPOINT
{
int iRet = 1;
CJView* pJV = new CJView(__argc,__argv);

if (!pJV)
{
CHAR szFmt[64];

LoadOemString(NULL, IDS_ERROR, szFmt, sizeof(szFmt));
fprintf(stderr, szFmt);
LoadOemString(NULL, IDS_OUTOFMEMORY, szFmt, sizeof(szFmt));
fprintf(stderr, szFmt);
fprintf(stderr, "\n");
return iRet;
}

if (pJV->Initialize())
{
// OK, we're ready, everything is done on the applet thread
HANDLE hth;
DWORD dwThreadID;

pJV->m_dwMsgLoopThreadID = GetCurrentThreadId();
hth = CreateThread(NULL, 0, &RunClassThread, pJV, 0, &dwThreadID);

if (hth)
{
RunMessageLoop();

// If we returned from RunMessageLoop() as a result of
// RunClassThread() posting the WM_QUIT message, then the thread
// will be exiting shortly (if not already). We wait for it to
// terminate and grab its exit code. 1/2 second is plenty --
// if the thread doesn't die by then, something is wrong (we
// got a quit message from someone else, perhaps?) in which case
// we return 1 for failure.

if (WaitForSingleObject (hth, 500) == WAIT_OBJECT_0)
{
DWORD dwRetCode = 1;

// Thread's dead, baby... thread's dead...
GetExitCodeThread (hth, &dwRetCode);
iRet = dwRetCode;
}
CloseHandle(hth);
hth = NULL;
}
else
{
pJV->m_FatalErrorHR(LAST_WIN32_ERROR_TO_HRESULT(),
IDS_NOJAVATHREAD);
}
}

delete pJV;
return iRet;
}