windows-nt/Source/XPSP1/NT/base/ntsetup/opktools/opklib/opklib.c
2020-09-26 16:20:57 +08:00

946 lines
26 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
opk.c
Abstract:
Common modules shared by OPK tools. Note: Source Depot requires that we
publish the .h (E:\NT\admin\published\ntsetup) and .lib
(E:\NT\public\internal\admin\lib).
Author:
Brian Ku (briank) 06/20/2000
Revision History:
7/00 - Jason Cohen (jcohen)
Added in the rest of the common APIs form Millennium (need for lfnbk).
--*/
//
// Include file(s)
//
#include <pch.h>
#include <tchar.h>
#include <shlwapi.h>
//
// External Function(s):
//
LPTSTR AllocateString(HINSTANCE hInstance, UINT uID)
{
// ISSUE-2002/02/26-acosma - This sets a restriction of 256 characters on the buffer.
//
TCHAR szBuffer[256];
LPTSTR lpBuffer = NULL;
// Load the string from the resource and then allocate
// a buffer just big enough for it. Strings can exceed 256 characters.
//
if ( ( LoadString(hInstance, uID, szBuffer, sizeof(szBuffer) / sizeof(TCHAR)) ) &&
( lpBuffer = (LPTSTR) MALLOC(sizeof(TCHAR) * (lstrlen(szBuffer) + 1)) ) )
{
lstrcpy(lpBuffer, szBuffer);
}
// Return the allocated buffer, or NULL if there was an error.
//
return lpBuffer;
}
LPTSTR AllocateExpand(LPTSTR lpszBuffer)
{
LPTSTR lpszExpanded = NULL;
DWORD cbExpanded;
// First we need to get the size of the expanded buffer and
// allocate it.
//
if ( ( cbExpanded = ExpandEnvironmentStrings(lpszBuffer, NULL, 0) ) &&
( lpszExpanded = (LPTSTR) MALLOC(cbExpanded * sizeof(TCHAR)) ) )
{
// Now expand out the buffer.
//
if ( ( 0 == ExpandEnvironmentStrings(lpszBuffer, lpszExpanded, cbExpanded) ) ||
( NULLCHR == *lpszExpanded ) )
{
FREE(lpszExpanded);
}
}
// Return the allocated buffer, or NULL if there was an error
// or nothing in the string.
//
return lpszExpanded;
}
LPTSTR AllocateStrRes(HINSTANCE hInstance, LPSTRRES lpsrTable, DWORD cbTable, LPTSTR lpString, LPTSTR * lplpReturn)
{
LPSTRRES lpsrSearch = lpsrTable;
LPTSTR lpReturn = NULL;
BOOL bFound;
// Init this return value.
//
if ( lplpReturn )
*lplpReturn = NULL;
// Try to find the friendly name for this string in our table.
//
while ( ( bFound = ((DWORD) (lpsrSearch - lpsrTable) < cbTable) ) &&
( lstrcmpi(lpString, lpsrSearch->lpStr) != 0 ) )
{
lpsrSearch++;
}
// If it was found, allocate the friendly name from the resource.
//
if ( bFound )
{
lpReturn = AllocateString(hInstance, lpsrSearch->uId);
if ( lplpReturn )
*lplpReturn = lpsrSearch->lpStr;
}
return lpReturn;
}
int MsgBoxLst(HWND hwndParent, LPTSTR lpFormat, LPTSTR lpCaption, UINT uType, va_list lpArgs)
{
INT nReturn;
DWORD dwCount = 0;
LPTSTR lpText = NULL;
// The format string is required.
//
if ( lpFormat )
{
do
{
// Allocate 1k of characters at a time.
//
dwCount += 1024;
// Free the previous buffer, if there was one.
//
FREE(lpText);
// Allocate a new buffer.
//
if ( lpText = MALLOC(dwCount * sizeof(TCHAR)) )
nReturn = _vsntprintf(lpText, dwCount, lpFormat, lpArgs);
else
nReturn = 0;
}
while ( nReturn < 0 );
// Make sure we have the format string.
//
if ( lpText )
{
// Display the message box.
//
nReturn = MessageBox(hwndParent, lpText, lpCaption, uType);
FREE(lpText);
}
}
else
nReturn = 0;
// Return the return value of the MessageBox() call. If there was a memory
// error, 0 will be returned.
//
return nReturn;
}
int MsgBoxStr(HWND hwndParent, LPTSTR lpFormat, LPTSTR lpCaption, UINT uType, ...)
{
va_list lpArgs;
// Initialize the lpArgs parameter with va_start().
//
va_start(lpArgs, uType);
// Return the return value of the MessageBox() call. If there was a memory
// error, 0 will be returned. This is all
//
return MsgBoxLst(hwndParent, lpFormat, lpCaption, uType, lpArgs);
}
int MsgBox(HWND hwndParent, UINT uFormat, UINT uCaption, UINT uType, ...)
{
va_list lpArgs;
INT nReturn;
LPTSTR lpFormat = NULL,
lpCaption = NULL;
// Initialize the lpArgs parameter with va_start().
//
va_start(lpArgs, uType);
// Get the format and caption strings from the resource.
//
if ( uFormat )
lpFormat = AllocateString(NULL, uFormat);
if ( uCaption )
lpCaption = AllocateString(NULL, uCaption);
// Return the return value of the MessageBox() call. If there was a memory
// error, 0 will be returned.
//
nReturn = MsgBoxLst(hwndParent, lpFormat, lpCaption, uType, lpArgs);
// Free the format and caption strings.
//
FREE(lpFormat);
FREE(lpCaption);
// Return the value saved from the previous function call.
//
return nReturn;
}
void CenterDialog(HWND hwnd)
{
CenterDialogEx(NULL, hwnd);
}
void CenterDialogEx(HWND hParent, HWND hChild)
{
RECT rcChild,
rcParent;
if ( GetWindowRect(hChild, &rcChild) )
{
// If parent is specified center with respect to parent.
if ( hParent && (GetWindowRect(hParent, &rcParent)) )
SetWindowPos(hChild, NULL, ((rcParent.right + rcParent.left - (rcChild.right - rcChild.left)) / 2), ((rcParent.bottom + rcParent.top - (rcChild.bottom - rcChild.top)) / 2), 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
// Otherwise center with respect to screen.
//
else
SetWindowPos(hChild, NULL, ((GetSystemMetrics(SM_CXSCREEN) - (rcChild.right - rcChild.left)) / 2), ((GetSystemMetrics(SM_CYSCREEN) - (rcChild.bottom - rcChild.top)) / 2), 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
}
}
INT_PTR CALLBACK SimpleDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
//CenterDialog(hwnd);
return FALSE;
case WM_COMMAND:
EndDialog(hwnd, LOWORD(wParam));
return FALSE;
default:
return FALSE;
}
return TRUE;
}
INT_PTR SimpleDialogBox(HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWndParent)
{
return DialogBox(hInstance, lpTemplate, hWndParent, SimpleDialogProc);
}
/****************************************************************************\
HFONT // Returns a valid handle to a font if it is
// successfully created, or NULL if something
// failed. The font handle should be deleteted
// with DeleteObject() when it is no longer
// needed.
GetFont( // This function creates a font based in the info
// passed in.
HWND hwndCtrl, // Handle to a control that is used for the
// default font characteristics. This may be
// NULL if not default is control is available.
LPTSTR lpFontName, // Points to a string that contains the name of
// the font to create. This parameter may be NULL
// if a valid control handle is passed in. In
// that case, the font of the control is used.
DWORD dwFontSize, // Point size to use for the font. If it is zero,
// the default is used.
BOOL bSymbol // If this is TRUE, the font is set to
// SYMBOL_CHARSET. Typically this is FALSE.
);
\****************************************************************************/
HFONT GetFont(HWND hwndCtrl, LPTSTR lpFontName, DWORD dwFontSize, LONG lFontWeight, BOOL bSymbol)
{
HFONT hFont;
LOGFONT lFont;
BOOL bGetFont;
// If the font name is passed in, then try to use that
// first before getting the font of the control.
//
if ( lpFontName && *lpFontName )
{
// Make sure the font name is not longer than
// 32 characters (including the NULL terminator).
//
if ( lstrlen(lpFontName) >= sizeof(lFont.lfFaceName) )
return NULL;
// Setup the structure to use to get the
// font we want.
//
ZeroMemory(&lFont, sizeof(LOGFONT));
lFont.lfCharSet = DEFAULT_CHARSET;
lstrcpy(lFont.lfFaceName, lpFontName);
}
// First try to get the font that we wanted.
//
if ( ( lpFontName == NULL ) ||
( *lpFontName == NULLCHR ) ||
( (hFont = CreateFontIndirect((LPLOGFONT) &lFont)) == NULL ) )
{
// Couldn't get the font we wanted, try the font of the control
// if a valid window handle was passed in.
//
if ( ( hwndCtrl == NULL ) ||
( (hFont = (HFONT) (WORD) SendMessage(hwndCtrl, WM_GETFONT, 0, 0L)) == NULL ) )
{
// All atempts to get the font failed. We must return NULL.
//
return NULL;
}
}
// Return the font we have now if we don't need to
// change the size or weight.
//
if ( (lFontWeight == 0) && (dwFontSize == 0) )
return hFont;
// We must have a valid HFONT now. Fill in the structure
// and setup the size and weight we wanted for it.
//
bGetFont = GetObject(hFont, sizeof(LOGFONT), (LPVOID) &lFont);
DeleteObject(hFont);
if ( bGetFont )
{
// Set the bold and point size of the font.
//
if ( lFontWeight )
lFont.lfWeight = lFontWeight;
if ( dwFontSize )
lFont.lfHeight = -MulDiv(dwFontSize, GetDeviceCaps(GetDC(NULL), LOGPIXELSY), 72);
if ( bSymbol )
lFont.lfCharSet = SYMBOL_CHARSET;
// Create the font.
//
hFont = CreateFontIndirect((LPLOGFONT) &lFont);
}
else
hFont = NULL;
return hFont;
}
void ShowEnableWindow(HWND hwnd, BOOL bShowEnable)
{
EnableWindow(hwnd, bShowEnable);
ShowWindow(hwnd, bShowEnable ? SW_SHOW : SW_HIDE);
}
/****************************************************************************\
BOOL // Returns TRUE if we are running a server OS.
IsServer( // This routine checks if we're running on a
// Server OS.
VOID
);
\****************************************************************************/
BOOL IsServer(VOID)
{
OSVERSIONINFOEX verInfo;
BOOL fReturn = FALSE;
verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if ( ( GetVersionEx((LPOSVERSIONINFO) &verInfo) ) &&
( verInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) &&
( ( verInfo.wProductType == VER_NT_SERVER ) ||
( verInfo.wProductType == VER_NT_DOMAIN_CONTROLLER ) ) )
{
fReturn = TRUE;
}
return fReturn;
}
BOOL IsIA64()
/*++
===============================================================================
Routine Description:
This routine checks if we're running on a 64-bit machine.
Arguments:
None -
Return Value:
TRUE - We are running on 64-bit machine.
FALSE - Not a 64-bit machine.
===============================================================================
--*/
{
BOOL fReturn = FALSE;
ULONG_PTR Wow64Info = 0;
DWORD dwSt = 0;
SYSTEM_INFO siSystemInfo;
ZeroMemory( &siSystemInfo, sizeof(SYSTEM_INFO) );
GetSystemInfo(&siSystemInfo);
if ( (siSystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) ||
(siSystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) )
fReturn = TRUE;
if (!fReturn)
{
// Now make sure that GetSystemInfo isn't lying because we are in emulation mode.
dwSt = NtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, &Wow64Info, sizeof(Wow64Info), NULL);
if (!NT_SUCCESS(dwSt))
{
// Handle to process is bad, or the code is compiled 32-bit and running on NT4 or earlier.
// Do nothing
}
else if (Wow64Info)
{
// The process is 32-bit and is running inside WOW64.
// We are really on IA64 and running in 32-bit mode.
fReturn = TRUE;
}
}
return fReturn;
}
BOOL ValidDosName(LPCTSTR lpName)
{
LPCTSTR lpSearch = lpName;
int nDot = 0,
nSpot = 0,
nLen;
// Do the easy checks.
//
if ( ( lpSearch == NULL ) ||
( *lpSearch == NULLCHR ) ||
( (nLen = lstrlen(lpSearch)) > 12 ) )
{
return FALSE;
}
// Search the string.
//
for (; *lpSearch; lpSearch++)
{
// Check for dots.
//
if ( *lpSearch == _T('.') )
{
// Keep track of the number of dots.
//
nDot++;
nSpot = (int) (lpSearch - lpName);
}
else
{
// Check for valid characters.
//
if ( !( ( ( _T('0') <= *lpSearch ) && ( *lpSearch <= _T('9') ) ) ||
( ( _T('a') <= *lpSearch ) && ( *lpSearch <= _T('z') ) ) ||
( ( _T('A') <= *lpSearch ) && ( *lpSearch <= _T('Z') ) ) ||
( *lpSearch == _T('-') ) ||
( *lpSearch == _T('_') ) ||
( *lpSearch == _T('~') ) ) )
{
// Invalid character.
//
return FALSE;
}
}
}
// Make sure the dot is in the right place.
//
if ( ( nDot > 1 ) ||
( ( nDot == 0 ) && ( nLen > 8 ) ) ||
( ( nDot == 1 ) && ( nSpot > 8 ) ) ||
( ( nDot == 1 ) && ( (nLen - nSpot) > 4 ) ) )
{
return FALSE;
}
return TRUE;
}
DWORD GetLineArgs(LPTSTR lpSrc, LPTSTR ** lplplpArgs, LPTSTR * lplpAllArgs)
{
LPTSTR lpCmdLine,
lpArg,
lpDst;
DWORD dwArgs = 0;
BOOL bQuote;
// Fist make sure that we were passed in a valid pointer, we have a command
// line to parse, and that we were able to allocate the memory to hold it.
//
if ( ( lplplpArgs != NULL ) &&
( lpSrc ) &&
( *lpSrc ) &&
( lpCmdLine = (LPTSTR) MALLOC((lstrlen(lpSrc) + 1) * sizeof(TCHAR)) ) )
{
// Fist parse the command line into NULL terminated sub strings.
//
lpDst = lpCmdLine;
while ( *lpSrc )
{
// Eat the preceeding spaces.
//
while ( *lpSrc == _T(' ') )
lpSrc = CharNext(lpSrc);
// Make sure we still have an argument.
//
if ( *lpSrc == _T('\0') )
break;
// Return a pointer to all the command line args if they want one.
//
if ( ( dwArgs == 1 ) && lplpAllArgs )
*lplpAllArgs = lpSrc;
// Save the current arg pointer.
//
lpArg = lpDst;
dwArgs++;
// See if we are looking for the next quote or space.
//
if ( bQuote = (*lpSrc == _T('"')) )
lpSrc = CharNext(lpSrc);
// Copy the argument into our allocated buffer until we
// hit the separating character (which will always be a space).
//
while ( *lpSrc && ( bQuote || ( *lpSrc != _T(' ') ) ) )
{
// We special case the quote.
//
if ( *lpSrc == _T('"') )
{
// If the character before the quote is a backslash, then
// we don't count this as the separating quote.
//
LPTSTR lpPrev = CharPrev(lpCmdLine, lpDst);
if ( lpPrev && ( *lpPrev == _T('\\') ) )
*lpPrev = *lpSrc++;
else
{
// Since we have found the separating quote, set this to
// false so we look for the next space.
//
bQuote = FALSE;
lpSrc++;
}
}
else
*lpDst++ = *lpSrc++;
}
// NULL terminate this argument.
//
*lpDst++ = _T('\0');
}
// Now setup the pointers to each argument. Make sure we have some arguments
// to return and that we have the memory allocated for the array.
//
if ( *lpCmdLine && dwArgs && ( *lplplpArgs = (LPTSTR *) MALLOC(dwArgs * sizeof(LPTSTR)) ) )
{
DWORD dwCount = 0;
// Copy a pointer to each NULL terminated sub string into our
// array of arguments we are going to return.
//
do
{
*(*lplplpArgs + dwCount) = lpCmdLine;
lpCmdLine += lstrlen(lpCmdLine) + 1;
}
while ( ++dwCount < dwArgs );
}
else
{
// Either there were no command line arguments, or the memory allocation
// failed for the list of arguments to return.
//
dwArgs = 0;
FREE(lpCmdLine);
}
}
return dwArgs;
}
DWORD GetCommandLineArgs(LPTSTR ** lplplpArgs)
{
return GetLineArgs(GetCommandLine(), lplplpArgs, NULL);
}
//
// Generic singularly linked list pvItem must be allocated with MALLOC
//
BOOL FAddListItem(PGENERIC_LIST* ppList, PGENERIC_LIST** pppNewItem, PVOID pvItem)
{
if (pppNewItem && *pppNewItem == NULL)
*pppNewItem = ppList;
if (*pppNewItem) {
if (**pppNewItem = (PGENERIC_LIST)MALLOC(sizeof(GENERIC_LIST))) {
(**pppNewItem)->pNext = NULL;
(**pppNewItem)->pvItem = pvItem;
*pppNewItem = &((**pppNewItem)->pNext);
return TRUE;
}
}
return FALSE;
}
void FreeList(PGENERIC_LIST pList)
{
while (pList) {
PGENERIC_LIST pTemp = pList;
pList = pList->pNext;
FREE(pTemp->pvItem);
FREE(pTemp);
}
}
// Find factory.exe from the current process, should be in same directory
//
BOOL FGetFactoryPath(LPTSTR pszFactoryPath)
{
// Attempt to locate FACTORY.EXE
//
// NTRAID#NTBUG9-549770-2002/02/26-acosma,georgeje - Possible buffer overflow.
//
if (pszFactoryPath && GetModuleFileName(NULL, pszFactoryPath, MAX_PATH)) {
if (PathRemoveFileSpec(pszFactoryPath)) {
PathAppend(pszFactoryPath, TEXT("FACTORY.EXE"));
if (FileExists(pszFactoryPath))
return TRUE;
}
}
return FALSE;
}
// Find sysprep.exe from the current process, should be in same directory
//
BOOL FGetSysprepPath(LPTSTR pszSysprepPath)
{
// Attempt to locate SYSPREP.EXE
//
// NTRAID#NTBUG9-549770-2002/02/26-acosma,georgeje - Possible buffer overflow.
//
if (pszSysprepPath && GetModuleFileName(NULL, pszSysprepPath, MAX_PATH)) {
if (PathRemoveFileSpec(pszSysprepPath)) {
PathAppend(pszSysprepPath, TEXT("SYSPREP.EXE"));
if (FileExists(pszSysprepPath))
return TRUE;
}
}
return FALSE;
}
//------------------------------------------------------------------------------------------------------
//
// Function: ConnectNetworkResource
//
// Purpose: This function allows the user to connect to a network resource with
// supplied credentials.
//
// Arguments: lpszPath: Network Resource that should be shared out
// lpszUsername: Username for credentials, can be in form of domain\username
// lpszPassword: Password to use for credentials
// bState: If set to TRUE we will add the connection, if FALSE we will
// attempt to delete the connection
//
// Returns: BOOL If the NetUse command was successful, TRUE is returned
//
//------------------------------------------------------------------------------------------------------
NET_API_STATUS ConnectNetworkResource(LPTSTR lpszPath, LPTSTR lpszUsername, LPTSTR lpszPassword, BOOL bState)
{
BOOL bRet = FALSE;
USE_INFO_2 ui2;
NET_API_STATUS nerr_NetUse;
TCHAR szDomain[MAX_PATH] = NULLSTR,
szNetUse[MAX_PATH] = NULLSTR;
LPTSTR lpUser,
lpSearch;
// Zero out the user information structure
//
ZeroMemory(&ui2, sizeof(ui2));
// Copy the path into our buffer so we can work with it
//
lstrcpyn(szNetUse, lpszPath, AS(szNetUse));
StrRTrm(szNetUse, CHR_BACKSLASH);
if ( szNetUse[0] && PathIsUNC(szNetUse) )
{
// Disconnect from existing share
//
nerr_NetUse = NetUseDel(NULL, szNetUse, USE_NOFORCE);
// We need to Add a connection
//
if ( bState )
{
ui2.ui2_remote = szNetUse;
ui2.ui2_asg_type = USE_DISKDEV;
ui2.ui2_password = lpszPassword;
lstrcpyn(szDomain, lpszUsername, AS(szDomain));
// Break up the Domain\Username for the NetUse function
//
if (lpUser = StrChr(szDomain, CHR_BACKSLASH) )
{
// Put a NULL character after the domain part of the user name
// and advance the pointer to point to the actual user name.
//
*(lpUser++) = NULLCHR;
}
else
{
// Use the computer name in the path as the domain name.
//
if ( lpSearch = StrChr(szNetUse + 2, CHR_BACKSLASH) )
lstrcpyn(szDomain, szNetUse + 2, (int)((lpSearch - (szNetUse + 2)) + 1));
else
lstrcpyn(szDomain, szNetUse + 2, AS(szDomain));
lpUser = lpszUsername;
}
// Set the domain and user name pointers into our struct.
//
ui2.ui2_domainname = szDomain;
ui2.ui2_username = lpUser;
// Create a connect to the share
//
nerr_NetUse = NetUseAdd(NULL, 2, (LPBYTE) &ui2, NULL);
}
}
else
nerr_NetUse = NERR_UseNotFound;
// Return failure/success
//
return nerr_NetUse;
}
BOOL GetUncShare(LPCTSTR lpszPath, LPTSTR lpszShare, DWORD cbShare)
{
BOOL bRet;
LPCTSTR lpSrc = lpszPath;
LPTSTR lpDst = lpszShare;
DWORD dwBackslashes,
dwCount;
// Make sure the path is a UNC by calling the shell function.
//
bRet = PathIsUNC(lpszPath);
// This will loop through the path string twice, each time coping all the
// backslashes and then all the non-backslashes. So if the string passed
// in was "\\COMPUTER\SHARE\DIR\FILE.NAME", the first pass will copy
// "\\COMPUTER" and the next pass would then copy "\SHARE" so the final
// string would then be "\\COMPUTER\SHARE". This loop also verifies there
// are the correct number of backslashes and non-backslashes. If there is
// no return buffer, then we just are verifing the share.
//
for ( dwBackslashes = 2; dwBackslashes && bRet; dwBackslashes-- )
{
// First copy the backslashes.
//
dwCount = 0;
while ( _T('\\') == *lpSrc )
{
if ( lpDst && cbShare )
{
*lpDst++ = *lpSrc;
cbShare--;
}
lpSrc++;
dwCount++;
}
// Make sure the number of backslashes is correct.
// The fist pass there should be two and the next
// pass should be just one.
//
if ( dwBackslashes != dwCount )
{
bRet = FALSE;
}
else
{
// Now copy the non-backslashes.
//
dwCount = 0;
while ( ( *lpSrc ) &&
( _T('\\') != *lpSrc ) )
{
if ( lpDst && cbShare )
{
*lpDst++ = *lpSrc;
cbShare--;
}
lpSrc++;
dwCount++;
}
// Make sure there was at least one non-backslash.
// character.
//
// Also if we are on the first pass and the path
// buffer is already empty, then we don't have the
// share part so just error out.
//
if ( ( 0 == dwCount ) ||
( ( 2 == dwBackslashes ) &&
( NULLCHR == *lpSrc ) ) )
{
bRet = FALSE;
}
}
}
// Only fix up the return buffer if there is one.
//
if ( lpszShare )
{
// Make sure that we didn't fail and that we still
// have room for the null terminator.
//
if ( bRet && cbShare )
{
// Don't forget to null terminate the return string.
//
*lpDst = NULLCHR;
}
else
{
// If we failed or ran out of buffer room, make sure
// we don't return anything.
//
*lpszShare = NULLCHR;
}
}
return bRet;
}
DWORD GetSkuType()
{
DWORD dwRet = 0;
OSVERSIONINFOEX osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if ( GetVersionEx((LPOSVERSIONINFO) &osvi) )
{
if ( VER_NT_WORKSTATION == osvi.wProductType )
{
if ( GET_FLAG(osvi.wSuiteMask, VER_SUITE_PERSONAL) )
{
dwRet = VER_SUITE_PERSONAL;
}
else
{
dwRet = VER_NT_WORKSTATION;
}
}
else
{
if ( GET_FLAG(osvi.wSuiteMask, VER_SUITE_DATACENTER) )
{
dwRet = VER_SUITE_DATACENTER;
}
else if ( GET_FLAG(osvi.wSuiteMask, VER_SUITE_ENTERPRISE) )
{
dwRet = VER_SUITE_ENTERPRISE;
}
else if ( GET_FLAG(osvi.wSuiteMask, VER_SUITE_BLADE) )
{
dwRet = VER_SUITE_BLADE;
}
else
{
dwRet = VER_NT_SERVER;
}
}
}
return dwRet;
}