You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3295 lines
86 KiB
3295 lines
86 KiB
/*++
|
|
Module Name:
|
|
|
|
Utils.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains the declaration for CWaitCursor class.
|
|
Contains utility methods which are used throughout the project.
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "resource.h"
|
|
#include "Utils.h"
|
|
#include "netutils.h"
|
|
#include <dns.h>
|
|
|
|
HRESULT
|
|
CWaitCursor::SetStandardCursor(
|
|
IN LPCTSTR i_lpCursorName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method sets the cursor to the standard cursor specified.
|
|
Usage: SetStandardCursor(IDC_WAIT)
|
|
|
|
Arguments:
|
|
|
|
i_lpCursorName - The name of a standard cursor, IDC_WAIT, IDC_ARROW.
|
|
|
|
--*/
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(i_lpCursorName);
|
|
|
|
HCURSOR m_hcur = ::LoadCursor(NULL, i_lpCursorName);
|
|
if (!m_hcur)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
::ShowCursor(FALSE);
|
|
SetCursor(m_hcur);
|
|
::ShowCursor(TRUE);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL
|
|
Is256ColorSupported(
|
|
VOID
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines whether the display supports 256 colors.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return value:
|
|
|
|
TRUE if display supports 256 colors
|
|
FALSE if not.
|
|
|
|
--*/
|
|
|
|
BOOL bRetval = FALSE;
|
|
|
|
HDC hdc = ::GetDC(NULL);
|
|
|
|
if( hdc )
|
|
{
|
|
if( ::GetDeviceCaps( hdc, BITSPIXEL ) >= 8 )
|
|
{
|
|
bRetval = TRUE;
|
|
}
|
|
::ReleaseDC(NULL, hdc);
|
|
}
|
|
return bRetval;
|
|
}
|
|
|
|
VOID
|
|
SetControlFont(
|
|
IN HFONT hFont,
|
|
IN HWND hwnd,
|
|
IN INT nId
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the text font of a dialog control to the input font.
|
|
|
|
Arguments:
|
|
|
|
hFont - The font to use.
|
|
|
|
hwnd - The parent dialog window.
|
|
|
|
nId - The control Id.
|
|
|
|
Return value:
|
|
|
|
None
|
|
|
|
--*/
|
|
if( hFont )
|
|
{
|
|
HWND hwndControl = ::GetDlgItem(hwnd, nId);
|
|
|
|
if( hwndControl )
|
|
::SendMessage(hwndControl, WM_SETFONT, (WPARAM) hFont, MAKELPARAM(TRUE, 0));
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SetupFonts(
|
|
IN HINSTANCE hInstance,
|
|
IN HWND hwnd,
|
|
IN HFONT *pBigBoldFont,
|
|
IN HFONT *pBoldFont
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates fonts for Wizard Titles.
|
|
|
|
Arguments:
|
|
|
|
hInstance - The module instance.
|
|
|
|
hwnd - The dialog window.
|
|
|
|
pBigBoldFont- The font for large title.
|
|
|
|
pBoldFont - The font for small title.
|
|
|
|
Return value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
NONCLIENTMETRICS ncm = {0};
|
|
ncm.cbSize = sizeof(ncm);
|
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
|
|
|
|
LOGFONT BigBoldLogFont = ncm.lfMessageFont;
|
|
LOGFONT BoldLogFont = ncm.lfMessageFont;
|
|
|
|
|
|
// Create Big Bold Font and Bold Font
|
|
|
|
BigBoldLogFont.lfWeight = FW_BOLD;
|
|
BoldLogFont.lfWeight = FW_BOLD;
|
|
|
|
TCHAR FontSizeString[24];
|
|
INT FontSize;
|
|
|
|
// Load size and name from resources, since these may change
|
|
// from locale to locale based on the size of the system font, etc.
|
|
|
|
if(!LoadString(hInstance,IDS_LARGEFONTNAME,BigBoldLogFont.lfFaceName,LF_FACESIZE))
|
|
{
|
|
lstrcpy(BigBoldLogFont.lfFaceName,TEXT("MS Shell Dlg"));
|
|
}
|
|
|
|
if(LoadString(hInstance,IDS_LARGEFONTSIZE,FontSizeString,sizeof(FontSizeString)/sizeof(TCHAR)))
|
|
{
|
|
FontSize = _tcstoul( FontSizeString, NULL, 10 );
|
|
}
|
|
else
|
|
{
|
|
FontSize = 18;
|
|
}
|
|
|
|
HDC hdc = ::GetDC( hwnd );
|
|
|
|
if( hdc )
|
|
{
|
|
BigBoldLogFont.lfHeight = 0 - (GetDeviceCaps(hdc,LOGPIXELSY) * FontSize / 72);
|
|
|
|
if (pBigBoldFont)
|
|
*pBigBoldFont = CreateFontIndirect(&BigBoldLogFont);
|
|
|
|
if (pBoldFont)
|
|
*pBoldFont = CreateFontIndirect(&BoldLogFont);
|
|
|
|
::ReleaseDC(hwnd,hdc);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DestroyFonts(
|
|
IN HFONT hBigBoldFont,
|
|
IN HFONT hBoldFont
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates fonts for Wizard Titles.
|
|
|
|
Arguments:
|
|
|
|
hBigBoldFont- The font for large title.
|
|
|
|
hBoldFont - The font for small title.
|
|
|
|
Return value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
if( hBigBoldFont )
|
|
{
|
|
DeleteObject( hBigBoldFont );
|
|
}
|
|
|
|
if( hBoldFont )
|
|
{
|
|
DeleteObject( hBoldFont );
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
LoadStringFromResource(
|
|
IN const UINT i_uResourceID,
|
|
OUT BSTR* o_pbstrReadValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method returns a resource string.
|
|
The method no longer uses a fixed string to read the resource.
|
|
Inspiration from MFC's CString::LoadString.
|
|
|
|
Arguments:
|
|
i_uResourceID - The resource id
|
|
o_pbstrReadValue - The BSTR* into which the value is copied
|
|
|
|
--*/
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(o_pbstrReadValue);
|
|
|
|
TCHAR szResString[1024];
|
|
ULONG uCopiedLen = 0;
|
|
|
|
szResString[0] = NULL;
|
|
|
|
// Read the string from the resource
|
|
uCopiedLen = ::LoadString(_Module.GetModuleInstance(), i_uResourceID, szResString, 1024);
|
|
|
|
// If nothing was copied it is flagged as an error
|
|
if(uCopiedLen <= 0)
|
|
{
|
|
return HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
else
|
|
{
|
|
*o_pbstrReadValue = ::SysAllocString(szResString);
|
|
if (!*o_pbstrReadValue)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
FormatResourceString(
|
|
IN const UINT i_uResourceID,
|
|
IN LPCTSTR i_szFirstArg,
|
|
OUT BSTR* o_pbstrReadString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads a string from resource, puts the argument into this string and
|
|
returns it.
|
|
The returned string should be freed using SysFreeString.
|
|
|
|
Arguments:
|
|
|
|
i_uResourceID - The resource id of the string to be read. This string
|
|
should contain a %1 to allow us to insert the argument
|
|
|
|
i_szFirstArg - The argument to be inserted
|
|
|
|
o_pbstrReadString - The string that is returned by the method after processing
|
|
--*/
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(i_szFirstArg);
|
|
RETURN_INVALIDARG_IF_NULL(o_pbstrReadString);
|
|
|
|
CComBSTR bstrResString;
|
|
LPTSTR lpszFormatedMessage = NULL;
|
|
|
|
HRESULT hr = LoadStringFromResource(i_uResourceID, &bstrResString);
|
|
RETURN_IF_FAILED(hr);
|
|
// Create a new string using the argument and the res string
|
|
int iBytes = ::FormatMessage(
|
|
FORMAT_MESSAGE_FROM_STRING |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER, // Format a string with %1, %2, etc
|
|
bstrResString, // Input buffer with a %1
|
|
0, // Message id. None
|
|
0, // Language id. Nothing particular
|
|
(LPTSTR)&lpszFormatedMessage, // Output buffer
|
|
0,
|
|
(va_list*)&i_szFirstArg // List of arguments. Only 1 right now
|
|
);
|
|
|
|
if (0 == iBytes)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
CComBSTR bstrRet(lpszFormatedMessage);
|
|
*o_pbstrReadString = bstrRet.Copy();
|
|
LocalFree(lpszFormatedMessage);
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
GetMessage(
|
|
OUT BSTR* o_pbstrMsg,
|
|
IN DWORD dwErr,
|
|
IN UINT iStringId, // OPTIONAL: String resource Id
|
|
...) // Optional arguments
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(o_pbstrMsg);
|
|
|
|
_ASSERT(dwErr != 0 || iStringId != 0); // One of the parameter must be non-zero
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
TCHAR szString[1024];
|
|
CComBSTR bstrErrorMsg, bstrResourceString, bstrMsg;
|
|
|
|
if (dwErr)
|
|
hr = GetErrorMessage(dwErr, &bstrErrorMsg);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (iStringId == 0)
|
|
{
|
|
bstrMsg = bstrErrorMsg;
|
|
}
|
|
else
|
|
{
|
|
::LoadString(_Module.GetModuleInstance(), iStringId,
|
|
szString, sizeof(szString)/sizeof(TCHAR));
|
|
|
|
va_list arglist;
|
|
va_start(arglist, iStringId);
|
|
LPTSTR lpBuffer = NULL;
|
|
DWORD dwRet = ::FormatMessage(
|
|
FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
szString,
|
|
0, // dwMessageId
|
|
0, // dwLanguageId, ignored
|
|
(LPTSTR)&lpBuffer,
|
|
0, // nSize
|
|
&arglist);
|
|
va_end(arglist);
|
|
|
|
if (dwRet == 0)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
bstrMsg = lpBuffer;
|
|
if (dwErr)
|
|
bstrMsg += bstrErrorMsg;
|
|
|
|
LocalFree(lpBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
// Failed to retrieve the proper message, report the failure directly to user
|
|
_stprintf(szString, _T("0x%x"), hr);
|
|
bstrMsg = szString;
|
|
}
|
|
|
|
*o_pbstrMsg = bstrMsg.Copy();
|
|
if (!*o_pbstrMsg)
|
|
return E_OUTOFMEMORY;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
int
|
|
DisplayMessageBox(
|
|
IN HWND hwndParent,
|
|
IN UINT uType, // style of message box
|
|
IN DWORD dwErr,
|
|
IN UINT iStringId, // OPTIONAL: String resource Id
|
|
...) // Optional arguments
|
|
{
|
|
_ASSERT(dwErr != 0 || iStringId != 0); // One of the parameter must be non-zero
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
TCHAR szCaption[1024], szString[1024];
|
|
CComBSTR bstrErrorMsg, bstrResourceString, bstrMsg;
|
|
|
|
::LoadString(_Module.GetModuleInstance(), IDS_APPLICATION_NAME,
|
|
szCaption, sizeof(szCaption)/sizeof(TCHAR));
|
|
|
|
if (dwErr)
|
|
hr = GetErrorMessage(dwErr, &bstrErrorMsg);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (iStringId == 0)
|
|
{
|
|
bstrMsg = bstrErrorMsg;
|
|
}
|
|
else
|
|
{
|
|
::LoadString(_Module.GetModuleInstance(), iStringId,
|
|
szString, sizeof(szString)/sizeof(TCHAR));
|
|
|
|
va_list arglist;
|
|
va_start(arglist, iStringId);
|
|
LPTSTR lpBuffer = NULL;
|
|
DWORD dwRet = ::FormatMessage(
|
|
FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
szString,
|
|
0, // dwMessageId
|
|
0, // dwLanguageId, ignored
|
|
(LPTSTR)&lpBuffer,
|
|
0, // nSize
|
|
&arglist);
|
|
va_end(arglist);
|
|
|
|
if (dwRet == 0)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
bstrMsg = lpBuffer;
|
|
if (dwErr)
|
|
bstrMsg += bstrErrorMsg;
|
|
|
|
LocalFree(lpBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
// Failed to retrieve the proper message, report the failure directly to user
|
|
_stprintf(szString, _T("0x%x"), hr);
|
|
bstrMsg = szString;
|
|
}
|
|
|
|
CThemeContextActivator activator;
|
|
return ::MessageBox(hwndParent, bstrMsg, szCaption, uType);
|
|
}
|
|
|
|
HRESULT
|
|
DisplayMessageBoxWithOK(
|
|
IN const int i_iMessageResID,
|
|
IN const BSTR i_bstrArgument/* = NULL*/
|
|
)
|
|
{
|
|
if (i_bstrArgument)
|
|
DisplayMessageBox(::GetActiveWindow(), MB_OK, 0, i_iMessageResID, i_bstrArgument);
|
|
else
|
|
DisplayMessageBox(::GetActiveWindow(), MB_OK, 0, i_iMessageResID);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
DisplayMessageBoxForHR(
|
|
IN HRESULT i_hr
|
|
)
|
|
{
|
|
DisplayMessageBox(::GetActiveWindow(), MB_OK, i_hr, 0);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CreateSmallImageList(
|
|
IN HINSTANCE i_hInstance,
|
|
IN int* i_pIconID,
|
|
IN const int i_nNumOfIcons,
|
|
OUT HIMAGELIST* o_phImageList
|
|
)
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(i_hInstance);
|
|
|
|
HRESULT hr = S_OK;
|
|
HIMAGELIST hImageList = ImageList_Create(
|
|
GetSystemMetrics(SM_CXSMICON),
|
|
GetSystemMetrics(SM_CYSMICON),
|
|
ILC_COLORDDB | ILC_MASK,
|
|
i_nNumOfIcons,
|
|
0);
|
|
if (!hImageList)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
return hr;
|
|
}
|
|
|
|
int i = 0;
|
|
for (i = 0; i < i_nNumOfIcons; i++)
|
|
{
|
|
HICON hIcon = LoadIcon(i_hInstance, MAKEINTRESOURCE(i_pIconID[i]));
|
|
if (!hIcon)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
}
|
|
|
|
if (-1 == ImageList_AddIcon(hImageList, hIcon))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (hImageList)
|
|
ImageList_Destroy(hImageList);
|
|
} else
|
|
{
|
|
if (o_phImageList)
|
|
*o_phImageList = hImageList;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
InsertIntoListView(
|
|
IN HWND i_hwndList,
|
|
IN LPCTSTR i_szItemText,
|
|
IN int i_iImageIndex /*= 0*/
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Insert and item into the listview. The image index for the item is optional
|
|
while the item text is necessary
|
|
|
|
Arguments:
|
|
|
|
i_hwndList - HWND of the list view
|
|
i_szItemText - The text for the item
|
|
i_iImageIndex - The image index for the item. Default is 0.
|
|
|
|
--*/
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(i_hwndList);
|
|
RETURN_INVALIDARG_IF_NULL(i_szItemText);
|
|
|
|
LVITEM lvi;
|
|
ZeroMemory(&lvi, sizeof(lvi));
|
|
lvi.mask = LVIF_TEXT | LVIF_IMAGE;
|
|
lvi.pszText = (LPTSTR)i_szItemText;
|
|
lvi.iImage = i_iImageIndex;
|
|
|
|
int iItemIndex = ListView_InsertItem(i_hwndList, &lvi); // Insert the item into the list view
|
|
if ( -1 == iItemIndex)
|
|
return E_FAIL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
GetListViewItemText(
|
|
IN HWND i_hwndListView,
|
|
IN int i_iItemID,
|
|
OUT BSTR* o_pbstrItemText
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Needed to write a method as the standard one has a slight problem.
|
|
Here, we make sure that string allocated is of proper length.
|
|
|
|
Arguments:
|
|
|
|
i_hwndList - HWND of the list view
|
|
i_iItemID - The ID of the item to be read
|
|
o_pbstrItemText - The item text returned by this method
|
|
--*/
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(i_hwndListView);
|
|
RETURN_INVALIDARG_IF_NULL(o_pbstrItemText);
|
|
|
|
*o_pbstrItemText = NULL;
|
|
if (-1 == i_iItemID)
|
|
return S_FALSE; // not a valid item index
|
|
|
|
LRESULT iReadTextLen = 0;
|
|
TCHAR szText[1024];
|
|
|
|
LVITEM lvItem;
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
lvItem.mask = LVIF_TEXT; // Initialize the LV item
|
|
lvItem.iItem = i_iItemID;
|
|
lvItem.pszText = szText;
|
|
lvItem.cchTextMax = 1024;
|
|
|
|
// Get the LV item text
|
|
iReadTextLen = SendMessage(i_hwndListView, LVM_GETITEMTEXT, lvItem.iItem, (LPARAM)&lvItem);
|
|
|
|
if(iReadTextLen <= 0)
|
|
{
|
|
return HRESULT_FROM_WIN32(::GetLastError());
|
|
}
|
|
else
|
|
{
|
|
*o_pbstrItemText = SysAllocString(szText);
|
|
if (!*o_pbstrItemText)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT GetComboBoxText(
|
|
IN HWND i_hwndCombo,
|
|
OUT BSTR* o_pbstrText
|
|
)
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(o_pbstrText);
|
|
|
|
int index = ::SendMessage(i_hwndCombo, CB_GETCURSEL, 0, 0);
|
|
int len = ::SendMessage(i_hwndCombo, CB_GETLBTEXTLEN, index, 0);
|
|
if (!len)
|
|
return S_FALSE; // no text
|
|
|
|
PTSTR pszText = (PTSTR)calloc(len + 1, sizeof(TCHAR));
|
|
RETURN_OUTOFMEMORY_IF_NULL(pszText);
|
|
|
|
::SendMessage(i_hwndCombo, CB_GETLBTEXT, index, (LPARAM)pszText);
|
|
|
|
*o_pbstrText = SysAllocString(pszText);
|
|
|
|
free(pszText);
|
|
|
|
RETURN_OUTOFMEMORY_IF_NULL(*o_pbstrText);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
EnableToolbarButtons(
|
|
IN const LPTOOLBAR i_lpToolbar,
|
|
IN const INT i_iFirstButtonID,
|
|
IN const INT i_iLastButtonID,
|
|
IN const BOOL i_bEnableState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enable or disable the toolbar buttons
|
|
|
|
Arguments:
|
|
|
|
i_lpToolbar - Callback used to do toolbar related operations
|
|
i_iFirstButtonID - The ID of the first button to be operated on.
|
|
i_iLastButtonID - The ID of the last button to be operated on.
|
|
i_bEnableState - The new state for enabled. Can be TRUE or FALSE
|
|
|
|
--*/
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(i_lpToolbar);
|
|
RETURN_INVALIDARG_IF_TRUE((i_iLastButtonID - i_iFirstButtonID) < 0);
|
|
|
|
for (int iCommandID = i_iFirstButtonID; iCommandID <= i_iLastButtonID; iCommandID++ )
|
|
{
|
|
i_lpToolbar->SetButtonState(iCommandID, ENABLED, i_bEnableState);
|
|
i_lpToolbar->SetButtonState(iCommandID, HIDDEN, !i_bEnableState);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AddBitmapToToolbar(
|
|
IN const LPTOOLBAR i_lpToolbar,
|
|
IN const INT i_iBitmapResource
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates and adds the bitmap to the toolbar.
|
|
This bitmap is used by the toolbar buttons.
|
|
|
|
Arguments:
|
|
|
|
i_lpToolbar - Callback used to do toolbar related operations
|
|
i_iBitmapResource - The resource id of the bitmap.
|
|
|
|
--*/
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(i_lpToolbar);
|
|
|
|
// Load the bitmap from resource
|
|
HBITMAP hBitmap = (HBITMAP)LoadImage(_Module.GetModuleInstance(), MAKEINTRESOURCE(i_iBitmapResource),
|
|
IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
|
|
if(!hBitmap)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
HRESULT hr = S_FALSE;
|
|
BITMAP bmpRec;
|
|
if (GetObject(hBitmap, sizeof(bmpRec), &bmpRec))
|
|
{
|
|
if (bmpRec.bmHeight > 0)
|
|
{
|
|
int icyBitmap = bmpRec.bmHeight;
|
|
int icxBitmap = icyBitmap; // Since the bitmaps are squares
|
|
int iNoOfBitmaps = bmpRec.bmWidth / bmpRec.bmHeight;
|
|
|
|
hr = i_lpToolbar->AddBitmap(iNoOfBitmaps, hBitmap, icxBitmap, icyBitmap,
|
|
RGB(255, 0, 255) // Pink is the mask color
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
DeleteObject(hBitmap);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT GetInputText(
|
|
IN HWND hwnd,
|
|
OUT BSTR* o_pbstrText,
|
|
OUT DWORD* o_pdwTextLength
|
|
)
|
|
{
|
|
_ASSERT(hwnd);
|
|
_ASSERT(o_pbstrText);
|
|
_ASSERT(o_pdwTextLength);
|
|
|
|
*o_pdwTextLength = 0;
|
|
*o_pbstrText = NULL;
|
|
|
|
HRESULT hr = S_OK;
|
|
int nLength = GetWindowTextLength(hwnd);
|
|
if (nLength == 0)
|
|
{
|
|
*o_pbstrText = SysAllocString(_T(""));
|
|
} else
|
|
{
|
|
PTSTR ptszText = (PTSTR)calloc(nLength+1, sizeof(TCHAR));
|
|
if (ptszText)
|
|
{
|
|
nLength = GetWindowText(hwnd, ptszText, nLength+1);
|
|
|
|
// trim right
|
|
PTSTR p = NULL;
|
|
for (p = ptszText + nLength - 1; p >= ptszText && _istspace(*p); p--)
|
|
{
|
|
*p = _T('\0');
|
|
}
|
|
|
|
// trim left
|
|
for (p = ptszText; *p && _istspace(*p); p++)
|
|
;
|
|
|
|
*o_pdwTextLength = _tcslen(p);
|
|
*o_pbstrText = SysAllocString(p);
|
|
|
|
free(ptszText);
|
|
}
|
|
}
|
|
|
|
if (!*o_pbstrText)
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
return hr;
|
|
}
|
|
|
|
// return FALSE, if value is not present or 0
|
|
// return TRUE, if value is present and non-zero
|
|
BOOL CheckRegKey()
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
LONG lErr = ERROR_SUCCESS;
|
|
HKEY hKey = 0;
|
|
|
|
lErr = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
_T("System\\CurrentControlSet\\Services\\Dfs"),
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (ERROR_SUCCESS == lErr)
|
|
{
|
|
DWORD dwType;
|
|
DWORD dwData = 0;
|
|
DWORD dwSize = sizeof(DWORD);
|
|
lErr = RegQueryValueEx(hKey, _T("DfsDnsConfig"), 0, &dwType, (LPBYTE)&dwData, &dwSize);
|
|
|
|
if (ERROR_SUCCESS == lErr && REG_DWORD == dwType && 0 != (dwData & 0x1))
|
|
bReturn = TRUE;
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
// called when adding a new junction point or adding a new replica member
|
|
BOOL
|
|
ValidateNetPath(
|
|
IN BSTR i_bstrNetPath,
|
|
OUT BSTR *o_pbstrServer,
|
|
OUT BSTR *o_pbstrShare
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bReturn = FALSE;
|
|
CComBSTR bstrServer;
|
|
CComBSTR bstrShare;
|
|
HWND hwnd = ::GetActiveWindow();
|
|
|
|
do {
|
|
// Check UNC path
|
|
hr = CheckUNCPath(i_bstrNetPath);
|
|
if (S_OK != hr)
|
|
{
|
|
DisplayMessageBox(hwnd, MB_OK, 0, IDS_NOT_UNC_PATH, i_bstrNetPath);
|
|
break;
|
|
}
|
|
|
|
CComBSTR bstrNetPath = i_bstrNetPath; // make a copy
|
|
|
|
// remove the ending backslash if any
|
|
TCHAR *p = bstrNetPath + lstrlen(bstrNetPath) - 1;
|
|
if (*p == _T('\\'))
|
|
*p = _T('\0');
|
|
/*
|
|
LinanT 6/2/2000:
|
|
a) add "check if path is contactable", warn user
|
|
*/
|
|
DWORD dwRet = GetFileAttributes(bstrNetPath);
|
|
if (-1 == dwRet)
|
|
{
|
|
if (IDYES != DisplayMessageBox(hwnd, MB_YESNO, GetLastError(), IDS_NETPATH_ADD_ANYWAY, i_bstrNetPath))
|
|
break;
|
|
} else if (!(dwRet & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
DisplayMessageBox(hwnd, MB_OK, 0, IDS_PATH_NOT_FOLDER, i_bstrNetPath);
|
|
break;
|
|
}
|
|
|
|
PTSTR lpszServer = bstrNetPath + 2; // skip the first "\\"
|
|
PTSTR lpszShare = _tcschr(lpszServer, _T('\\'));
|
|
if (!lpszShare)
|
|
break;
|
|
*lpszShare++ = _T('\0');
|
|
bstrShare = lpszShare;
|
|
|
|
/*
|
|
LinanT 3/19/99:
|
|
a) remove "check if path is contactable", leave it to dfs API
|
|
b) remove "get dns server name":
|
|
c) add code to do simple check for dots, if non-dns-look, pop up dialog for confirmation
|
|
*/
|
|
bstrServer = lpszServer;
|
|
if ( CheckRegKey() &&
|
|
NULL == _tcschr(bstrServer, _T('.')) &&
|
|
IDYES != DisplayMessageBox(hwnd, MB_YESNO, 0,
|
|
IDS_NON_DNSNAME_ADD_ANYWAY, i_bstrNetPath) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
bReturn = TRUE;
|
|
|
|
} while (0);
|
|
|
|
if (bReturn)
|
|
{
|
|
if ( !(*o_pbstrServer = bstrServer.Copy()) ||
|
|
!(*o_pbstrShare = bstrShare.Copy()) )
|
|
{
|
|
bReturn = FALSE;
|
|
DisplayMessageBox(hwnd, MB_OK | MB_ICONSTOP, (DWORD)E_OUTOFMEMORY, 0);
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// IsLocalComputername(): cut & pasted from ..\..\framewrk\islocal.cpp
|
|
//
|
|
TCHAR g_achComputerName[ MAX_COMPUTERNAME_LENGTH+1 ] = _T("");
|
|
TCHAR g_achDnsComputerName[DNS_MAX_NAME_BUFFER_LENGTH] = _T("");
|
|
|
|
BOOL
|
|
IsLocalComputername( IN LPCTSTR pszMachineName )
|
|
{
|
|
if ( NULL == pszMachineName || _T('\0') == pszMachineName[0] )
|
|
return TRUE;
|
|
|
|
if ( _T('\\') == pszMachineName[0] && _T('\\') == pszMachineName[1] )
|
|
pszMachineName += 2;
|
|
|
|
// compare with the local computer netbios name
|
|
if ( _T('\0') == g_achComputerName[0] )
|
|
{
|
|
DWORD dwSize = sizeof(g_achComputerName)/sizeof(TCHAR);
|
|
GetComputerName( g_achComputerName, &dwSize );
|
|
_ASSERT(_T('\0') != g_achComputerName[0]);
|
|
}
|
|
if ( 0 == lstrcmpi( pszMachineName, g_achComputerName ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// compare with the local DNS name
|
|
// SKwan confirms that ComputerNameDnsFullyQualified is the right name to use
|
|
// when clustering is taken into account
|
|
if ( _T('\0') == g_achDnsComputerName[0] )
|
|
{
|
|
DWORD dwSize = sizeof(g_achDnsComputerName)/sizeof(TCHAR);
|
|
GetComputerNameEx(
|
|
ComputerNameDnsFullyQualified,
|
|
g_achDnsComputerName,
|
|
&dwSize );
|
|
_ASSERT( _T('\0') != g_achDnsComputerName[0] );
|
|
}
|
|
if ( 0 == lstrcmpi( pszMachineName, g_achDnsComputerName ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // IsLocalComputername()
|
|
|
|
// S_OK: a local computer
|
|
// S_FALSE: not a local computer
|
|
HRESULT
|
|
IsComputerLocal(
|
|
IN LPCTSTR lpszServer
|
|
)
|
|
{
|
|
return (IsLocalComputername(lpszServer) ? S_OK : S_FALSE);
|
|
}
|
|
|
|
BOOL
|
|
IsValidLocalAbsolutePath(
|
|
IN LPCTSTR lpszPath
|
|
)
|
|
{
|
|
DWORD dwPathType = 0;
|
|
DWORD dwStatus = I_NetPathType(
|
|
NULL,
|
|
const_cast<LPTSTR>(lpszPath),
|
|
&dwPathType,
|
|
0);
|
|
if (dwStatus)
|
|
return FALSE;
|
|
|
|
if (dwPathType ^ ITYPE_PATH_ABSD)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// This function will return the full path with the \\?\ prefix.
|
|
// That is, \\?\X:\a\b\c if local, or \\?\UNC\server\X$\a\b\c if remote.
|
|
//
|
|
HRESULT
|
|
GetFullPath(
|
|
IN LPCTSTR lpszServer,
|
|
IN LPCTSTR lpszPath,
|
|
OUT BSTR *o_pbstrFullPath
|
|
)
|
|
{
|
|
_ASSERT(IsValidLocalAbsolutePath(lpszPath));
|
|
|
|
CComBSTR bstrFullPath;
|
|
if (S_OK == IsComputerLocal(lpszServer))
|
|
{
|
|
bstrFullPath = _T("\\\\?\\");
|
|
bstrFullPath += lpszPath;
|
|
} else
|
|
{
|
|
bstrFullPath = _T("\\\\?\\UNC\\");
|
|
if (mylstrncmpi(_T("\\\\"), lpszServer, 2))
|
|
{
|
|
bstrFullPath += lpszServer;
|
|
} else
|
|
{
|
|
bstrFullPath += lpszServer + 2;
|
|
}
|
|
bstrFullPath += _T("\\");
|
|
bstrFullPath += lpszPath;
|
|
|
|
TCHAR *p = _tcschr(bstrFullPath, _T(':'));
|
|
if (p)
|
|
{
|
|
*p = _T('$');
|
|
}
|
|
}
|
|
|
|
*o_pbstrFullPath = bstrFullPath.Detach();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Purpose: verify if the specified drive belongs to a list of disk drives on the server
|
|
// Return:
|
|
// S_OK: yes
|
|
// S_FALSE: no
|
|
// hr: some error happened
|
|
HRESULT
|
|
VerifyDriveLetter(
|
|
IN LPCTSTR lpszServer,
|
|
IN LPCTSTR lpszPath
|
|
)
|
|
{
|
|
_ASSERT(IsValidLocalAbsolutePath(lpszPath));
|
|
HRESULT hr = S_FALSE;
|
|
LPBYTE pBuffer = NULL;
|
|
DWORD dwEntriesRead = 0;
|
|
DWORD dwTotalEntries = 0;
|
|
DWORD dwRet = NetServerDiskEnum(
|
|
const_cast<LPTSTR>(lpszServer),
|
|
0,
|
|
&pBuffer,
|
|
(DWORD)-1,
|
|
&dwEntriesRead,
|
|
&dwTotalEntries,
|
|
NULL);
|
|
|
|
if (NERR_Success == dwRet)
|
|
{
|
|
LPTSTR pDrive = (LPTSTR)pBuffer;
|
|
for (UINT i=0; i<dwEntriesRead; i++)
|
|
{
|
|
if (!mylstrncmpi(pDrive, lpszPath, 1))
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
pDrive += 3;
|
|
}
|
|
|
|
NetApiBufferFree(pBuffer);
|
|
} else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwRet);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Purpose: is there a related admin $ share
|
|
// Return:
|
|
// S_OK: yes
|
|
// S_FALSE: no
|
|
// hr: some error happened
|
|
HRESULT
|
|
IsAdminShare(
|
|
IN LPCTSTR lpszServer,
|
|
IN LPCTSTR lpszPath
|
|
)
|
|
{
|
|
_ASSERT(S_OK != IsComputerLocal(lpszServer));
|
|
_ASSERT(IsValidLocalAbsolutePath(lpszPath));
|
|
|
|
HRESULT hr = S_FALSE;
|
|
LPBYTE pBuffer = NULL;
|
|
DWORD dwEntriesRead = 0;
|
|
DWORD dwTotalEntries = 0;
|
|
DWORD dwRet = NetShareEnum(
|
|
const_cast<LPTSTR>(lpszServer),
|
|
1,
|
|
&pBuffer,
|
|
(DWORD)-1,
|
|
&dwEntriesRead,
|
|
&dwTotalEntries,
|
|
NULL);
|
|
|
|
if (NERR_Success == dwRet)
|
|
{
|
|
PSHARE_INFO_1 pShareInfo = (PSHARE_INFO_1)pBuffer;
|
|
for (UINT i=0; i<dwEntriesRead; i++)
|
|
{
|
|
if ( (pShareInfo->shi1_type & STYPE_SPECIAL) &&
|
|
_tcslen(pShareInfo->shi1_netname) == 2 &&
|
|
*(pShareInfo->shi1_netname + 1) == _T('$') &&
|
|
!mylstrncmpi(pShareInfo->shi1_netname, lpszPath, 1) )
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
pShareInfo++;
|
|
}
|
|
|
|
NetApiBufferFree(pBuffer);
|
|
} else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwRet);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: IsAnExistingFolder
|
|
//
|
|
// Synopsis: Check if pszPath is pointing at an existing folder.
|
|
//
|
|
// S_OK: The specified path points to an existing folder.
|
|
// S_FALSE: The specified path doesn't point to an existing folder.
|
|
// hr: Failed to get info on the specified path, or
|
|
// the path exists but doesn't point to a folder.
|
|
// The function reports error msg for both failures if desired.
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
IsAnExistingFolder(
|
|
IN HWND hwnd,
|
|
IN LPCTSTR pszPath // points to path with "\\?\" prefix
|
|
)
|
|
{
|
|
if (!hwnd)
|
|
hwnd = GetActiveWindow();
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA fad = {0};
|
|
if (!GetFileAttributesEx(pszPath, GetFileExInfoStandard, &fad))
|
|
{
|
|
DWORD dwErr = GetLastError();
|
|
if (ERROR_PATH_NOT_FOUND == dwErr || ERROR_FILE_NOT_FOUND == dwErr)
|
|
{
|
|
// the specified path doesn't exist
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
DisplayMessageBox(hwnd, MB_OK, dwErr, IDS_FAILED_TO_GETINFO_FOLDER, pszPath);
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
} else if ( 0 == (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
|
|
{
|
|
// the specified path is not pointing to a folder
|
|
DisplayMessageBox(hwnd, MB_OK, 0, IDS_PATH_NOT_FOLDER, pszPath);
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// create the directories layer by layer
|
|
HRESULT
|
|
CreateLayeredDirectory(
|
|
IN LPCTSTR lpszServer,
|
|
IN LPCTSTR lpszPath
|
|
)
|
|
{
|
|
_ASSERT(IsValidLocalAbsolutePath(lpszPath));
|
|
|
|
//
|
|
// get the full path with \\?\ prefix, such that CreateDirectory
|
|
// will turn off path parsing in case our path is longer than MAX_PATH
|
|
//
|
|
CComBSTR bstrFullPath;
|
|
GetFullPath(lpszServer, lpszPath, &bstrFullPath);
|
|
|
|
LPTSTR p = _tcschr(bstrFullPath,
|
|
(S_OK == IsComputerLocal(lpszServer)) ? _T(':') : _T('$'));
|
|
|
|
//
|
|
// bstrFullPath is either "\\?\C:\a\b\c\d" or "\\?\UNC\server\C$\a\b\c\d"
|
|
// move p to point at "a\b\c\d"
|
|
//
|
|
p += 2;
|
|
|
|
BOOL bRet = TRUE;
|
|
while (p && *p)
|
|
{
|
|
p = _tcschr(p, _T('\\'));
|
|
|
|
if (p)
|
|
*p = _T('\0');
|
|
|
|
bRet = CreateDirectory(bstrFullPath, NULL);
|
|
if (!bRet)
|
|
{
|
|
DWORD dwErr = GetLastError();
|
|
if (dwErr != ERROR_ALREADY_EXISTS)
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|
|
if (p)
|
|
*p++ = _T('\\'); // restore the backslash, move p to point at the char after the backslash
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
BrowseNetworkPath(
|
|
IN HWND hwndParent,
|
|
OUT BSTR *o_pbstrPath
|
|
)
|
|
{
|
|
_ASSERT(o_pbstrPath);
|
|
|
|
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
do
|
|
{
|
|
CComPtr<IMalloc> pMalloc;
|
|
hr = SHGetMalloc(&pMalloc);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
CComBSTR bstrDlgLabel;
|
|
hr = LoadStringFromResource(IDS_BROWSE_NET_DLG, &bstrDlgLabel);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
LPITEMIDLIST pItemIdList = NULL;
|
|
hr = SHGetSpecialFolderLocation(NULL, CSIDL_NETWORK, &pItemIdList);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
BROWSEINFO bi = {hwndParent,
|
|
pItemIdList,
|
|
0,
|
|
bstrDlgLabel,
|
|
BIF_RETURNONLYFSDIRS,
|
|
NULL,
|
|
NULL,
|
|
0};
|
|
|
|
LPITEMIDLIST pItemIdListBr = SHBrowseForFolder(&bi);
|
|
if (!pItemIdListBr)
|
|
{
|
|
hr = S_FALSE; // user clicked Cancel
|
|
} else
|
|
{
|
|
CComBSTR bstrPath;
|
|
TCHAR szPath[MAX_PATH] = _T("\0");
|
|
SHGetPathFromIDList(pItemIdListBr, szPath);
|
|
|
|
//
|
|
// try to use Dns server name
|
|
//
|
|
if (CheckRegKey() &&
|
|
S_OK == CheckUNCPath(szPath))
|
|
{
|
|
PTSTR lpszServer = szPath + 2; // skip the first "\\"
|
|
PTSTR lpszShare = _tcschr(lpszServer, _T('\\'));
|
|
CComBSTR bstrServer = CComBSTR(lpszShare - lpszServer, lpszServer);
|
|
CComBSTR bstrDnsServer;
|
|
hr = GetServerInfo(bstrServer,
|
|
NULL, // Domain
|
|
NULL, // NetbiosName
|
|
NULL, // bValidDSObject
|
|
&bstrDnsServer);
|
|
if (S_OK == hr)
|
|
{
|
|
bstrPath = _T("\\\\");
|
|
bstrPath += bstrDnsServer;
|
|
bstrPath += lpszShare;
|
|
} else
|
|
{
|
|
hr = S_OK; // reset hr
|
|
bstrPath = szPath;
|
|
}
|
|
} else
|
|
{
|
|
bstrPath = szPath;
|
|
}
|
|
|
|
*o_pbstrPath = bstrPath.Detach();
|
|
|
|
pMalloc->Free(pItemIdListBr);
|
|
}
|
|
|
|
pMalloc->Free(pItemIdList);
|
|
|
|
} while (0);
|
|
|
|
CoUninitialize();
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
DisplayMessageBox(hwndParent, MB_OK, hr, IDS_FAILED_TO_BROWSE_NETWORKPATH);
|
|
|
|
return hr;
|
|
}
|
|
#define MAX_DFS_REFERRAL_TIME 0xFFFFFFFF
|
|
|
|
BOOL
|
|
ValidateTimeout(
|
|
IN LPCTSTR lpszTimeout,
|
|
OUT ULONG *pulTimeout
|
|
)
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
|
|
if (pulTimeout)
|
|
{
|
|
*pulTimeout = 0;
|
|
|
|
__int64 i64Timeout = _wtoi64(lpszTimeout);
|
|
|
|
if (i64Timeout <= MAX_DFS_REFERRAL_TIME)
|
|
{
|
|
bReturn = TRUE;
|
|
*pulTimeout = (ULONG)i64Timeout;
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
#include "winnetp.h"
|
|
|
|
// retrieve system drive letter on the specified machine
|
|
HRESULT GetSystemDrive(IN LPCTSTR lpszComputer, OUT TCHAR *ptch)
|
|
{
|
|
_ASSERT(ptch);
|
|
|
|
HRESULT hr = S_OK;
|
|
SHARE_INFO_2* pShareInfo = NULL;
|
|
NET_API_STATUS nstatRetVal = NetShareGetInfo(
|
|
const_cast<LPTSTR>(lpszComputer),
|
|
_T("Admin$"),
|
|
2,
|
|
(LPBYTE *)&pShareInfo);
|
|
|
|
if (nstatRetVal == NERR_Success)
|
|
{
|
|
_ASSERT(_T(':') == *(pShareInfo->shi2_path + 1));
|
|
*ptch = *(pShareInfo->shi2_path);
|
|
} else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(nstatRetVal);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// return a drive letter X, the staging path will be created at <X>:\FRS-Staging
|
|
// Try to exclude the following drives for performance consideration:
|
|
// 1. system drive: because the jet database ntfrs uses resides on system drive
|
|
// 2. the drive the replica folder sits on
|
|
// Will try to return a drive with the most free space
|
|
//
|
|
TCHAR
|
|
GetDiskForStagingPath(
|
|
IN LPCTSTR i_lpszServer,
|
|
IN TCHAR i_tch
|
|
)
|
|
{
|
|
_ASSERT(i_lpszServer && *i_lpszServer);
|
|
_ASSERT(_istalpha(i_tch));
|
|
|
|
TCHAR tchDrive = i_tch;
|
|
|
|
//
|
|
// retrieve the system drive letter on the specified machine
|
|
//
|
|
TCHAR tchSystemDrive;
|
|
if (S_OK != GetSystemDrive(i_lpszServer, &tchSystemDrive))
|
|
return tchDrive;
|
|
|
|
//
|
|
// enumerate all shareable disks, e.g., \\server\C$, \\server\D$, etc.
|
|
//
|
|
CComBSTR bstrServer;
|
|
if (mylstrncmpi(i_lpszServer, _T("\\\\"), 2))
|
|
{
|
|
bstrServer = _T("\\\\");
|
|
bstrServer += i_lpszServer;
|
|
} else
|
|
bstrServer = i_lpszServer;
|
|
|
|
NETRESOURCE nr;
|
|
nr.dwScope = RESOURCE_SHAREABLE;
|
|
nr.dwType = RESOURCETYPE_ANY;
|
|
nr.dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
|
|
nr.dwUsage = RESOURCEUSAGE_CONTAINER;
|
|
nr.lpLocalName = _T("");
|
|
nr.lpRemoteName = bstrServer;
|
|
nr.lpComment = _T("");
|
|
nr.lpProvider = _T("");
|
|
|
|
HANDLE hEnum = NULL;
|
|
DWORD dwResult = WNetOpenEnum (
|
|
RESOURCE_SHAREABLE,
|
|
RESOURCETYPE_ANY,
|
|
RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER,
|
|
&nr,
|
|
&hEnum);
|
|
if (dwResult == NO_ERROR)
|
|
{
|
|
NETRESOURCE nrBuffer[26];
|
|
DWORD dwBufferSize = 26 * sizeof(NETRESOURCE);
|
|
DWORD dwNumEntries = 0xFFFFFFFF; // Enumerate all possible entries.
|
|
dwResult = WNetEnumResource (
|
|
hEnum,
|
|
&dwNumEntries,
|
|
nrBuffer,
|
|
&dwBufferSize);
|
|
|
|
if (dwResult == NO_ERROR)
|
|
{
|
|
ULONGLONG ullFreeSpace = 0;
|
|
for (DWORD dwIndex = 0; dwIndex < dwNumEntries; dwIndex++)
|
|
{
|
|
//
|
|
// lpRemoteName contains string in the form of \\server\C$
|
|
//
|
|
TCHAR *p = nrBuffer[dwIndex].lpRemoteName;
|
|
TCHAR tchCurrent = *(p + _tcslen(p) - 2);
|
|
|
|
//
|
|
// exclude the current drive specified in i_tch
|
|
//
|
|
if ( _totupper(i_tch) == _totupper(tchCurrent) )
|
|
continue;
|
|
|
|
//
|
|
// skip if it's not a NTFS file system that supports object identifiers
|
|
//
|
|
TCHAR szFileSystemName[MAX_PATH + 1];
|
|
DWORD dwMaxCompLength = 0, dwFileSystemFlags = 0;
|
|
CComBSTR bstrRootPath = p;
|
|
if (_T('\\') != *(p + _tcslen(p) - 1))
|
|
bstrRootPath += _T("\\");
|
|
if (FALSE == GetVolumeInformation(bstrRootPath, NULL, 0, NULL, &dwMaxCompLength,
|
|
&dwFileSystemFlags, szFileSystemName, MAX_PATH))
|
|
continue;
|
|
|
|
if (CSTR_EQUAL != CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, _T("NTFS"), -1, szFileSystemName, -1) ||
|
|
!(FILE_SUPPORTS_OBJECT_IDS & dwFileSystemFlags))
|
|
continue;
|
|
|
|
//
|
|
// 1. when i_tch is on a non-system drive and system drive is NTFS,
|
|
// change default to system drive
|
|
// 2. when other NTFS drives present, exclude system drive
|
|
//
|
|
if ( _totupper(tchSystemDrive) == _totupper(tchCurrent) )
|
|
{
|
|
if ( 0 == ullFreeSpace )
|
|
tchDrive = tchSystemDrive;
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// find out the drive that has the most free space
|
|
//
|
|
ULARGE_INTEGER ulgiFreeBytesAvailableToCaller;
|
|
ULARGE_INTEGER ulgiTotalNumberOfBytes;
|
|
|
|
if (GetDiskFreeSpaceEx(p,
|
|
&ulgiFreeBytesAvailableToCaller,
|
|
&ulgiTotalNumberOfBytes,
|
|
NULL))
|
|
{
|
|
if (ulgiFreeBytesAvailableToCaller.QuadPart > ullFreeSpace)
|
|
{
|
|
tchDrive = tchCurrent;
|
|
ullFreeSpace = ulgiFreeBytesAvailableToCaller.QuadPart;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
WNetCloseEnum (hEnum);
|
|
}
|
|
|
|
return tchDrive;
|
|
}
|
|
|
|
HRESULT GetUNCPath
|
|
(
|
|
IN BSTR i_bstrServerName,
|
|
IN BSTR i_bstrShareName,
|
|
OUT BSTR* o_pbstrUNCPath
|
|
)
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(i_bstrServerName);
|
|
RETURN_INVALIDARG_IF_NULL(i_bstrShareName);
|
|
RETURN_INVALIDARG_IF_NULL(o_pbstrUNCPath);
|
|
|
|
CComBSTR bstrUNCPath;
|
|
|
|
bstrUNCPath = _T("\\\\");
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrUNCPath);
|
|
bstrUNCPath += i_bstrServerName;
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrUNCPath);
|
|
bstrUNCPath += _T("\\");
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrUNCPath);
|
|
bstrUNCPath += i_bstrShareName;
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrUNCPath);
|
|
|
|
*o_pbstrUNCPath = bstrUNCPath.Detach();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT GetDfsRootDisplayName
|
|
(
|
|
IN BSTR i_bstrScopeName,
|
|
IN BSTR i_bstrDfsName,
|
|
OUT BSTR* o_pbstrDisplayName
|
|
)
|
|
{
|
|
return GetUNCPath(i_bstrScopeName, i_bstrDfsName, o_pbstrDisplayName);
|
|
}
|
|
|
|
HRESULT GetDfsReplicaDisplayName
|
|
(
|
|
IN BSTR i_bstrServerName,
|
|
IN BSTR i_bstrShareName,
|
|
OUT BSTR* o_pbstrDisplayName
|
|
)
|
|
{
|
|
return GetUNCPath(i_bstrServerName, i_bstrShareName, o_pbstrDisplayName);
|
|
}
|
|
|
|
HRESULT
|
|
AddLVColumns(
|
|
IN const HWND hwndListBox,
|
|
IN const INT iStartingResourceID,
|
|
IN const UINT uiColumns
|
|
)
|
|
{
|
|
//
|
|
// calculate the listview column width
|
|
//
|
|
RECT rect;
|
|
ZeroMemory(&rect, sizeof(rect));
|
|
::GetWindowRect(hwndListBox, &rect);
|
|
int nControlWidth = rect.right - rect.left;
|
|
int nVScrollbarWidth = GetSystemMetrics(SM_CXVSCROLL);
|
|
int nBorderWidth = GetSystemMetrics(SM_CXBORDER);
|
|
int nControlNetWidth = nControlWidth - 4 * nBorderWidth;
|
|
int nWidth = nControlNetWidth / uiColumns;
|
|
|
|
LVCOLUMN lvColumn;
|
|
ZeroMemory(&lvColumn, sizeof(lvColumn));
|
|
lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
|
|
|
|
lvColumn.fmt = LVCFMT_LEFT;
|
|
lvColumn.cx = nWidth;
|
|
|
|
for (UINT i = 0; i < uiColumns; i++)
|
|
{
|
|
CComBSTR bstrColumnText;
|
|
|
|
LoadStringFromResource(iStartingResourceID + i, &bstrColumnText);
|
|
|
|
lvColumn.pszText = bstrColumnText;
|
|
lvColumn.iSubItem = i;
|
|
|
|
ListView_InsertColumn(hwndListBox, i, &lvColumn);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
LPARAM GetListViewItemData(
|
|
IN HWND hwndList,
|
|
IN int index
|
|
)
|
|
{
|
|
if (-1 == index)
|
|
return NULL;
|
|
|
|
LVITEM lvItem;
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
|
|
lvItem.mask = LVIF_PARAM;
|
|
lvItem.iItem = index;
|
|
if (ListView_GetItem(hwndList, &lvItem))
|
|
return lvItem.lParam;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
HRESULT CreateAndHideStagingPath(
|
|
IN BSTR i_bstrServer,
|
|
IN BSTR i_bstrStagingPath
|
|
)
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(i_bstrServer);
|
|
RETURN_INVALIDARG_IF_NULL(i_bstrStagingPath);
|
|
|
|
//
|
|
// Create the directory
|
|
//
|
|
HRESULT hr = CreateLayeredDirectory(i_bstrServer, i_bstrStagingPath);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
//
|
|
// try to hide the staging directory, ignore errors
|
|
//
|
|
CComBSTR bstrFullPath;
|
|
GetFullPath(i_bstrServer, i_bstrStagingPath, &bstrFullPath);
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA fad = {0};
|
|
if (GetFileAttributesEx(bstrFullPath, GetFileExInfoStandard, &fad))
|
|
{
|
|
(void) SetFileAttributes(bstrFullPath, fad.dwFileAttributes | FILE_ATTRIBUTE_HIDDEN);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: ConfigAndStartNtfrs
|
|
//
|
|
// Synopsis: Config ntfrs to be AUTO_START, and start the service.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT
|
|
ConfigAndStartNtfrs
|
|
(
|
|
BSTR i_bstrServer
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SC_HANDLE hScManager = NULL;
|
|
SC_HANDLE hService = NULL;
|
|
SERVICE_STATUS svcStatus;
|
|
DWORD dwDesiredAccess = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_START;
|
|
|
|
do
|
|
{
|
|
if ((hScManager = ::OpenSCManager(i_bstrServer, NULL, SC_MANAGER_CONNECT )) == NULL ||
|
|
(hService = ::OpenService(hScManager, _T("ntfrs"), dwDesiredAccess)) == NULL ||
|
|
!ChangeServiceConfig(hService, SERVICE_NO_CHANGE, SERVICE_AUTO_START, SERVICE_NO_CHANGE,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL) ||
|
|
!::QueryServiceStatus(hService, &svcStatus) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
}
|
|
|
|
if (SERVICE_RUNNING != svcStatus.dwCurrentState)
|
|
{
|
|
if (!StartService(hService, 0, NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
}
|
|
|
|
// The following is a cut&paste from MSDN article
|
|
// Check the status until the service is no longer start pending.
|
|
if (!QueryServiceStatus(hService,&svcStatus))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
break;
|
|
}
|
|
|
|
// Get the tick count before entering the loop.
|
|
DWORD dwStartTickCount = GetTickCount();
|
|
DWORD dwOldCheckPoint = svcStatus.dwCheckPoint;
|
|
DWORD dwWaitTime;
|
|
|
|
while (svcStatus.dwCurrentState == SERVICE_START_PENDING)
|
|
{
|
|
|
|
// Do not wait longer than the wait hint. A good interval is
|
|
// one tenth the wait hint, but no less than 1 second and no
|
|
// more than 10 seconds.
|
|
|
|
dwWaitTime = svcStatus.dwWaitHint / 10;
|
|
|
|
if ( dwWaitTime < 1000 )
|
|
dwWaitTime = 1000;
|
|
else if ( dwWaitTime > 10000 )
|
|
dwWaitTime = 10000;
|
|
|
|
Sleep( dwWaitTime );
|
|
|
|
// Check the status again.
|
|
if (!QueryServiceStatus(hService, &svcStatus))
|
|
break;
|
|
|
|
if (svcStatus.dwCheckPoint > dwOldCheckPoint)
|
|
{
|
|
// The service is making progress
|
|
|
|
dwStartTickCount = GetTickCount();
|
|
dwOldCheckPoint = svcStatus.dwCheckPoint;
|
|
}
|
|
else
|
|
{
|
|
if (GetTickCount() - dwStartTickCount > svcStatus.dwWaitHint)
|
|
{
|
|
// No progress made within the wait hint
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (svcStatus.dwCurrentState == SERVICE_RUNNING)
|
|
hr = S_OK;
|
|
else
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
if (hService)
|
|
CloseServiceHandle(hService);
|
|
if (hScManager)
|
|
CloseServiceHandle(hScManager);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: CheckResourceProvider
|
|
//
|
|
// Synopsis: see if pszResource is provided by "Microsoft Windows Network".
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT
|
|
CheckResourceProvider(LPCTSTR pszResource)
|
|
{
|
|
DWORD dwError = 0;
|
|
NETRESOURCE nr = {0};
|
|
NETRESOURCE nrOut = {0};
|
|
LPTSTR pszSystem = NULL; // pointer to variable-length strings
|
|
NETRESOURCE *pBuffer = &nrOut; // buffer
|
|
DWORD cbResult = sizeof(nrOut); // buffer size
|
|
|
|
nr.dwScope = RESOURCE_GLOBALNET;
|
|
nr.dwType = RESOURCETYPE_DISK;
|
|
nr.lpRemoteName = (LPTSTR)pszResource;
|
|
|
|
//
|
|
// Find the right provider string for "Microsoft Windows Network".
|
|
//
|
|
// Network provider string is localizable. In order to support localized
|
|
// system or MUI on ENG system, we need to retrieve the name from system
|
|
// instead of load the string from a resource file.
|
|
//
|
|
TCHAR szProviderName[MAX_PATH];
|
|
DWORD dwNumOfChars = MAX_PATH;
|
|
PTSTR pszProviderName = szProviderName;
|
|
dwError = WNetGetProviderName(WNNC_NET_LANMAN, pszProviderName, &dwNumOfChars);
|
|
if (dwError == ERROR_MORE_DATA)
|
|
{
|
|
pszProviderName = (PTSTR)LocalAlloc(LMEM_FIXED, dwNumOfChars * sizeof(TCHAR));
|
|
if (!pszProviderName)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
dwError = WNetGetProviderName(WNNC_NET_LANMAN, pszProviderName, &dwNumOfChars);
|
|
}
|
|
}
|
|
|
|
if (dwError == NO_ERROR)
|
|
{
|
|
nr.lpProvider = pszProviderName;
|
|
|
|
//
|
|
// First call the WNetGetResourceInformation function with
|
|
// memory allocated to hold only a NETRESOURCE structure. This
|
|
// method can succeed if all the NETRESOURCE pointers are NULL.
|
|
//
|
|
dwError = WNetGetResourceInformation(&nr, (LPBYTE)pBuffer, &cbResult, &pszSystem);
|
|
|
|
//
|
|
// If the call fails because the buffer is too small,
|
|
// call the LocalAlloc function to allocate a larger buffer.
|
|
//
|
|
if (dwError == ERROR_MORE_DATA)
|
|
{
|
|
pBuffer = (NETRESOURCE *)LocalAlloc(LMEM_FIXED, cbResult);
|
|
|
|
if (!pBuffer)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
} else
|
|
{
|
|
// Call WNetGetResourceInformation again with the larger buffer.
|
|
dwError = WNetGetResourceInformation(&nr, (LPBYTE)pBuffer, &cbResult, &pszSystem);
|
|
}
|
|
}
|
|
|
|
if (dwError == NO_ERROR)
|
|
{
|
|
// If the call succeeds, process the contents of the
|
|
// returned NETRESOURCE structure and the variable-length
|
|
// strings in lpBuffer. Then free the memory.
|
|
//
|
|
if (pBuffer != &nrOut)
|
|
{
|
|
LocalFree(pBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pszProviderName && pszProviderName != szProviderName)
|
|
{
|
|
LocalFree(pszProviderName);
|
|
}
|
|
|
|
return (dwError == NO_ERROR ? S_OK : HRESULT_FROM_WIN32(dwError));
|
|
}
|
|
|
|
HRESULT FRSShareCheck
|
|
(
|
|
BSTR i_bstrServer,
|
|
BSTR i_bstrFolder,
|
|
OUT FRSSHARE_TYPE *pFRSShareType
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Performs FRS checks for the share to be able particiapte in a FRS set.
|
|
Arguments:
|
|
i_bstrServer - The server hosting the share
|
|
i_bstrFolder - The share path.
|
|
--*/
|
|
{
|
|
_ASSERT(i_bstrServer && *i_bstrServer && i_bstrFolder && *i_bstrFolder && pFRSShareType);
|
|
|
|
// Is the server a NT 5.0 server with FRS?
|
|
HRESULT hr = S_FALSE;
|
|
hr = FRSIsNTFRSInstalled(i_bstrServer);
|
|
if (S_FALSE == hr)
|
|
{
|
|
*pFRSShareType = FRSSHARE_TYPE_NONTFRS;
|
|
return hr;
|
|
} else if (FAILED(hr))
|
|
{
|
|
*pFRSShareType = FRSSHARE_TYPE_UNKNOWN;
|
|
return hr;
|
|
}
|
|
|
|
// Is the path on a valid disktree share?
|
|
hr = GetFolderInfo(i_bstrServer, i_bstrFolder);
|
|
if (STG_E_NOTFILEBASEDSTORAGE == hr)
|
|
{
|
|
*pFRSShareType = FRSSHARE_TYPE_NOTDISKTREE;
|
|
return S_FALSE;
|
|
} else if (FAILED(hr))
|
|
{
|
|
*pFRSShareType = FRSSHARE_TYPE_UNKNOWN;
|
|
return hr;
|
|
}
|
|
|
|
// Get root path as \\server\share
|
|
CComBSTR bstrRootPath = _T("\\\\");
|
|
bstrRootPath+= i_bstrServer;
|
|
bstrRootPath+= _T("\\");
|
|
TCHAR *p = _tcschr(i_bstrFolder, _T('\\'));
|
|
if (p)
|
|
{
|
|
bstrRootPath += CComBSTR(p - i_bstrFolder + 1, i_bstrFolder);
|
|
} else
|
|
{
|
|
bstrRootPath += i_bstrFolder;
|
|
bstrRootPath+= _T("\\");
|
|
}
|
|
|
|
TCHAR szFileSystemName[MAX_PATH];
|
|
DWORD dwMaxCompLength = 0, dwFileSystemFlags = 0;
|
|
|
|
_ASSERT(bstrRootPath);
|
|
|
|
// on NTFS file system that supports object identifiers?
|
|
if (0 == GetVolumeInformation(
|
|
bstrRootPath, // Volume path
|
|
NULL, // Volume name not required
|
|
0, // Size of volume name buffer
|
|
NULL, // Serial number not required.
|
|
&dwMaxCompLength,
|
|
&dwFileSystemFlags,
|
|
szFileSystemName,
|
|
MAX_PATH
|
|
))
|
|
{
|
|
*pFRSShareType = FRSSHARE_TYPE_UNKNOWN;
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
if (CSTR_EQUAL != CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, _T("NTFS"), -1, szFileSystemName, -1) || !(FILE_SUPPORTS_OBJECT_IDS & dwFileSystemFlags))
|
|
{
|
|
*pFRSShareType = FRSSHARE_TYPE_NOTNTFS;
|
|
return S_FALSE;
|
|
}
|
|
|
|
*pFRSShareType = FRSSHARE_TYPE_OK;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT FRSIsNTFRSInstalled
|
|
(
|
|
BSTR i_bstrServer
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Checks if the computer has ntfrs service.
|
|
Arguments:
|
|
i_bstrServer - The name of the server.
|
|
Return value:
|
|
S_OK, if server has ntfrs service.
|
|
S_FALSE, if server does not have ntfrs service installed.
|
|
--*/
|
|
{
|
|
if (!i_bstrServer)
|
|
return(E_INVALIDARG);
|
|
|
|
SC_HANDLE SCMHandle = NULL, NTFRSHandle = NULL;
|
|
HRESULT hr = S_FALSE;
|
|
|
|
SCMHandle = OpenSCManager (i_bstrServer, NULL, SC_MANAGER_CONNECT);
|
|
if (!SCMHandle)
|
|
return(HRESULT_FROM_WIN32(GetLastError()));
|
|
|
|
NTFRSHandle = OpenService (
|
|
SCMHandle,
|
|
_T("ntfrs"),
|
|
SERVICE_QUERY_STATUS
|
|
);
|
|
if (!NTFRSHandle)
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
if (ERROR_SERVICE_DOES_NOT_EXIST == dwError)
|
|
hr = S_FALSE;
|
|
else
|
|
hr = HRESULT_FROM_WIN32(dwError);
|
|
|
|
CloseServiceHandle(SCMHandle);
|
|
return(hr);
|
|
} else
|
|
hr = S_OK;
|
|
|
|
CloseServiceHandle(NTFRSHandle);
|
|
CloseServiceHandle(SCMHandle);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
typedef HRESULT (*pfnReplicationScheduleDialogEx)
|
|
(
|
|
HWND hwndParent, // parent window
|
|
BYTE ** pprgbData, // pointer to pointer to array of 84 bytes
|
|
LPCTSTR pszTitle, // dialog title
|
|
DWORD dwFlags // option flags
|
|
);
|
|
|
|
static HINSTANCE g_hDllSchedule = NULL;
|
|
static pfnReplicationScheduleDialogEx g_hProcSchedule = NULL;
|
|
|
|
//
|
|
// S_OK: button OK is clicked and the new schedule is returned in io_pSchedule
|
|
// S_FALSE: button Cancle is clicked, io_pSchedule is not touched
|
|
//
|
|
HRESULT InvokeScheduleDlg(
|
|
IN HWND i_hwndParent,
|
|
IN OUT SCHEDULE* io_pSchedule
|
|
)
|
|
{
|
|
CComBSTR bstrTitle;
|
|
HRESULT hr = LoadStringFromResource(IDS_SCHEDULE, &bstrTitle);
|
|
RETURN_IF_FAILED(hr);
|
|
|
|
//
|
|
// LoadLibrary
|
|
//
|
|
if (!g_hDllSchedule)
|
|
{
|
|
if (!(g_hDllSchedule = LoadLibrary(_T("loghours.dll"))) ||
|
|
!(g_hProcSchedule = (pfnReplicationScheduleDialogEx)GetProcAddress(g_hDllSchedule, "ReplicationScheduleDialogEx")) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
if (g_hDllSchedule)
|
|
{
|
|
FreeLibrary(g_hDllSchedule);
|
|
g_hDllSchedule = NULL;
|
|
}
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// invoke the schedule dialog
|
|
//
|
|
BYTE* pbScheduleData = (BYTE *)io_pSchedule + io_pSchedule->Schedules->Offset;
|
|
hr = (*g_hProcSchedule)(i_hwndParent, &pbScheduleData, bstrTitle, 0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT TranslateManagedBy(
|
|
IN PCTSTR i_pszDC,
|
|
IN PCTSTR i_pszIn,
|
|
OUT BSTR* o_pbstrOut,
|
|
IN DS_NAME_FORMAT i_formatIn,
|
|
IN DS_NAME_FORMAT i_formatOut
|
|
)
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(o_pbstrOut);
|
|
|
|
*o_pbstrOut = NULL;
|
|
|
|
HRESULT hr = S_OK;
|
|
if (!i_pszIn || !*i_pszIn)
|
|
return hr;
|
|
|
|
CComBSTR bstr;
|
|
HANDLE hDS = NULL;
|
|
DWORD dwErr = DsBind(i_pszDC, NULL, &hDS);
|
|
if (ERROR_SUCCESS != dwErr)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
} else
|
|
{
|
|
hr = CrackName( hDS,
|
|
(PTSTR)i_pszIn,
|
|
i_formatIn,
|
|
i_formatOut,
|
|
&bstr
|
|
);
|
|
DsUnBind(&hDS);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
*o_pbstrOut = bstr.Detach();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT GetFTDfsObjectDN(
|
|
IN PCTSTR i_pszDomainName,
|
|
IN PCTSTR i_pszRootName,
|
|
OUT BSTR* o_pbstrFTDfsObjectDN
|
|
)
|
|
{
|
|
CComBSTR bstrDomainDN;
|
|
HRESULT hr = GetDomainInfo(
|
|
i_pszDomainName,
|
|
NULL, // return DC's Dns name
|
|
NULL, // return Domain's Dns name
|
|
&bstrDomainDN // return DC=nttest,DC=micr
|
|
);
|
|
RETURN_IF_FAILED(hr);
|
|
|
|
CComBSTR bstrFTDfsObjectDN = _T("CN=");
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrFTDfsObjectDN);
|
|
bstrFTDfsObjectDN += i_pszRootName;
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrFTDfsObjectDN);
|
|
bstrFTDfsObjectDN += _T(",");
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrFTDfsObjectDN);
|
|
bstrFTDfsObjectDN += _T("CN=Dfs-Configuration,CN=System,");
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrFTDfsObjectDN);
|
|
bstrFTDfsObjectDN += bstrDomainDN;
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrFTDfsObjectDN);
|
|
|
|
*o_pbstrFTDfsObjectDN = bstrFTDfsObjectDN.Detach();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ReadSharePublishInfoHelper(
|
|
PLDAP i_pldap,
|
|
LPCTSTR i_pszDN,
|
|
LPCTSTR i_pszSearchFilter,
|
|
OUT BOOL* o_pbPublish,
|
|
OUT BSTR* o_pbstrUNCPath,
|
|
OUT BSTR* o_pbstrDescription,
|
|
OUT BSTR* o_pbstrKeywords,
|
|
OUT BSTR* o_pbstrManagedBy)
|
|
{
|
|
dfsDebugOut((_T("ReadSharePublishInfoHelper %s %s\n"),
|
|
i_pszDN, i_pszSearchFilter));
|
|
|
|
*o_pbPublish = FALSE;
|
|
|
|
HRESULT hr = S_OK;
|
|
hr = IsValidObject(i_pldap, (PTSTR)i_pszDN);
|
|
if (S_OK != hr)
|
|
return hr;
|
|
|
|
LListElem* pElem = NULL;
|
|
|
|
do {
|
|
PCTSTR ppszAttributes[] = {
|
|
ATTR_SHRPUB_UNCNAME,
|
|
ATTR_SHRPUB_DESCRIPTION,
|
|
ATTR_SHRPUB_KEYWORDS,
|
|
ATTR_SHRPUB_MANAGEDBY,
|
|
0
|
|
};
|
|
|
|
hr = GetValuesEx(
|
|
i_pldap,
|
|
i_pszDN,
|
|
LDAP_SCOPE_BASE,
|
|
i_pszSearchFilter,
|
|
ppszAttributes,
|
|
&pElem);
|
|
RETURN_IF_FAILED(hr);
|
|
|
|
if (!pElem || !pElem->pppszAttrValues)
|
|
return hr;
|
|
|
|
PTSTR** pppszValues = pElem->pppszAttrValues;
|
|
|
|
if (pppszValues[0] && *(pppszValues[0]))
|
|
{
|
|
*o_pbstrUNCPath = SysAllocString(*(pppszValues[0]));
|
|
BREAK_OUTOFMEMORY_IF_NULL(*o_pbstrUNCPath, &hr);
|
|
*o_pbPublish = TRUE;
|
|
}
|
|
|
|
if (pppszValues[1] && *(pppszValues[1]))
|
|
{
|
|
*o_pbstrDescription = SysAllocString(*(pppszValues[1]));
|
|
BREAK_OUTOFMEMORY_IF_NULL(*o_pbstrDescription, &hr);
|
|
}
|
|
|
|
if (pppszValues[2] && *(pppszValues[2]))
|
|
{
|
|
CComBSTR bstrKeywords;
|
|
PTSTR *ppszStrings = pppszValues[2];
|
|
while (*ppszStrings)
|
|
{
|
|
if (!bstrKeywords)
|
|
{
|
|
bstrKeywords = *ppszStrings;
|
|
BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrKeywords, &hr);
|
|
} else
|
|
{
|
|
bstrKeywords += _T(";");
|
|
BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrKeywords, &hr);
|
|
bstrKeywords += *ppszStrings;
|
|
BREAK_OUTOFMEMORY_IF_NULL((BSTR)bstrKeywords, &hr);
|
|
}
|
|
ppszStrings++;
|
|
}
|
|
*o_pbstrKeywords = bstrKeywords.Detach();
|
|
}
|
|
|
|
if (pppszValues[3] && *(pppszValues[3]))
|
|
{
|
|
*o_pbstrManagedBy = SysAllocString(*(pppszValues[3]));
|
|
BREAK_OUTOFMEMORY_IF_NULL(*o_pbstrManagedBy, &hr);
|
|
}
|
|
|
|
} while (0);
|
|
|
|
if (pElem)
|
|
FreeLListElem(pElem);
|
|
|
|
return hr;
|
|
}
|
|
HRESULT ReadSharePublishInfoOnFTRoot(
|
|
LPCTSTR i_pszDomainName,
|
|
LPCTSTR i_pszRootName,
|
|
OUT BOOL* o_pbPublish,
|
|
OUT BSTR* o_pbstrUNCPath,
|
|
OUT BSTR* o_pbstrDescription,
|
|
OUT BSTR* o_pbstrKeywords,
|
|
OUT BSTR* o_pbstrManagedBy)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CComBSTR bstrFTDfsObjectDN;
|
|
hr = GetFTDfsObjectDN(i_pszDomainName, i_pszRootName, &bstrFTDfsObjectDN);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
CComBSTR bstrDC;
|
|
PLDAP pldap = NULL;
|
|
hr = ConnectToDS(i_pszDomainName, &pldap, &bstrDC); // PDC is preferred
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CComBSTR bstrManagedByFQDN;
|
|
hr = ReadSharePublishInfoHelper(
|
|
pldap,
|
|
bstrFTDfsObjectDN,
|
|
OBJCLASS_SF_FTDFS,
|
|
o_pbPublish,
|
|
o_pbstrUNCPath,
|
|
o_pbstrDescription,
|
|
o_pbstrKeywords,
|
|
&bstrManagedByFQDN);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = TranslateManagedBy(bstrDC,
|
|
bstrManagedByFQDN,
|
|
o_pbstrManagedBy,
|
|
DS_FQDN_1779_NAME,
|
|
DS_USER_PRINCIPAL_NAME);
|
|
if (FAILED(hr))
|
|
hr = TranslateManagedBy(bstrDC,
|
|
bstrManagedByFQDN,
|
|
o_pbstrManagedBy,
|
|
DS_FQDN_1779_NAME,
|
|
DS_NT4_ACCOUNT_NAME);
|
|
}
|
|
|
|
CloseConnectionToDS(pldap);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ReadSharePublishInfoOnSARoot(
|
|
LPCTSTR i_pszServerName,
|
|
LPCTSTR i_pszShareName,
|
|
OUT BOOL* o_pbPublish,
|
|
OUT BSTR* o_pbstrUNCPath,
|
|
OUT BSTR* o_pbstrDescription,
|
|
OUT BSTR* o_pbstrKeywords,
|
|
OUT BSTR* o_pbstrManagedBy)
|
|
{
|
|
RETURN_INVALIDARG_IF_NULL(i_pszServerName);
|
|
RETURN_INVALIDARG_IF_NULL(i_pszShareName);
|
|
RETURN_INVALIDARG_IF_NULL(o_pbPublish);
|
|
RETURN_INVALIDARG_IF_NULL(o_pbstrUNCPath);
|
|
RETURN_INVALIDARG_IF_NULL(o_pbstrDescription);
|
|
RETURN_INVALIDARG_IF_NULL(o_pbstrKeywords);
|
|
RETURN_INVALIDARG_IF_NULL(o_pbstrManagedBy);
|
|
|
|
*o_pbPublish = FALSE;
|
|
*o_pbstrUNCPath = NULL;
|
|
*o_pbstrDescription = NULL;
|
|
*o_pbstrKeywords = NULL;
|
|
*o_pbstrManagedBy = NULL;
|
|
|
|
CComBSTR bstrDomainName, bstrFQDN;
|
|
HRESULT hr = GetServerInfo(
|
|
(PTSTR)i_pszServerName,
|
|
&bstrDomainName,
|
|
NULL, //NetbiosName
|
|
NULL, //ValidDSObject
|
|
NULL, //DnsName,
|
|
NULL, //Guid,
|
|
&bstrFQDN);
|
|
if (S_OK != hr)
|
|
return hr;
|
|
|
|
CComBSTR bstrVolumeObjectDN = _T("CN=");
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrVolumeObjectDN);
|
|
bstrVolumeObjectDN += i_pszShareName;
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrVolumeObjectDN);
|
|
bstrVolumeObjectDN += _T(",");
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrVolumeObjectDN);
|
|
bstrVolumeObjectDN += bstrFQDN;
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrVolumeObjectDN);
|
|
|
|
CComBSTR bstrDC;
|
|
PLDAP pldap = NULL;
|
|
hr = ConnectToDS(bstrDomainName, &pldap, &bstrDC);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CComBSTR bstrManagedByFQDN;
|
|
hr = ReadSharePublishInfoHelper(
|
|
pldap,
|
|
bstrVolumeObjectDN,
|
|
OBJCLASS_SF_VOLUME,
|
|
o_pbPublish,
|
|
o_pbstrUNCPath,
|
|
o_pbstrDescription,
|
|
o_pbstrKeywords,
|
|
&bstrManagedByFQDN);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = TranslateManagedBy(bstrDC,
|
|
bstrManagedByFQDN,
|
|
o_pbstrManagedBy,
|
|
DS_FQDN_1779_NAME,
|
|
DS_USER_PRINCIPAL_NAME);
|
|
if (FAILED(hr))
|
|
hr = TranslateManagedBy(bstrDC,
|
|
bstrManagedByFQDN,
|
|
o_pbstrManagedBy,
|
|
DS_FQDN_1779_NAME,
|
|
DS_NT4_ACCOUNT_NAME);
|
|
}
|
|
|
|
CloseConnectionToDS(pldap);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CreateVolumeObject(
|
|
PLDAP i_pldap,
|
|
PCTSTR i_pszDN,
|
|
PCTSTR i_pszUNCPath,
|
|
PCTSTR i_pszDescription,
|
|
PCTSTR i_pszKeywords,
|
|
PCTSTR i_pszManagedBy)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
LDAP_ATTR_VALUE pAttrVals[5];
|
|
|
|
int i =0;
|
|
pAttrVals[i].bstrAttribute = OBJCLASS_ATTRIBUTENAME;
|
|
pAttrVals[i].vpValue = (void *)OBJCLASS_VOLUME;
|
|
pAttrVals[i].bBerValue = false;
|
|
i++;
|
|
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_UNCNAME;
|
|
pAttrVals[i].vpValue = (void *)i_pszUNCPath;
|
|
pAttrVals[i].bBerValue = false;
|
|
i++;
|
|
|
|
if (i_pszDescription && *i_pszDescription)
|
|
{
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_DESCRIPTION;
|
|
pAttrVals[i].vpValue = (void *)i_pszDescription;
|
|
pAttrVals[i].bBerValue = false;
|
|
i++;
|
|
}
|
|
|
|
LDAP_ATTR_VALUE *pHead = NULL;
|
|
if (i_pszKeywords && *i_pszKeywords)
|
|
{
|
|
hr = PutMultiValuesIntoAttrValList(i_pszKeywords, &pHead);
|
|
if (S_OK == hr)
|
|
{
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_KEYWORDS;
|
|
pAttrVals[i].vpValue = (void *)pHead->vpValue; // multi-valued
|
|
pAttrVals[i].bBerValue = false;
|
|
pAttrVals[i].Next = pHead->Next;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (i_pszManagedBy && *i_pszManagedBy)
|
|
{
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_MANAGEDBY;
|
|
pAttrVals[i].vpValue = (void *)i_pszManagedBy;
|
|
pAttrVals[i].bBerValue = false;
|
|
i++;
|
|
}
|
|
|
|
hr = AddValues(i_pldap, i_pszDN, i, pAttrVals);
|
|
|
|
if (pHead)
|
|
FreeAttrValList(pHead);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ModifyShareObject(
|
|
PLDAP i_pldap,
|
|
PCTSTR i_pszDN,
|
|
PCTSTR i_pszUNCPath,
|
|
PCTSTR i_pszDescription,
|
|
PCTSTR i_pszKeywords,
|
|
PCTSTR i_pszManagedBy)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = IsValidObject(i_pldap, (PTSTR)i_pszDN);
|
|
if (S_OK != hr)
|
|
return hr;
|
|
|
|
LDAP_ATTR_VALUE pAttrVals[4];
|
|
ZeroMemory(pAttrVals, sizeof(pAttrVals));
|
|
|
|
//
|
|
// modify values if any
|
|
//
|
|
int i =0;
|
|
if (i_pszUNCPath && *i_pszUNCPath)
|
|
{
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_UNCNAME;
|
|
pAttrVals[i].vpValue = (void *)i_pszUNCPath;
|
|
pAttrVals[i].bBerValue = false;
|
|
i++;
|
|
}
|
|
|
|
if (i_pszDescription && *i_pszDescription)
|
|
{
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_DESCRIPTION;
|
|
pAttrVals[i].vpValue = (void *)i_pszDescription;
|
|
pAttrVals[i].bBerValue = false;
|
|
i++;
|
|
}
|
|
|
|
LDAP_ATTR_VALUE *pHead = NULL;
|
|
if (i_pszKeywords && *i_pszKeywords)
|
|
{
|
|
hr = PutMultiValuesIntoAttrValList(i_pszKeywords, &pHead);
|
|
if (S_OK == hr)
|
|
{
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_KEYWORDS;
|
|
pAttrVals[i].vpValue = (void *)pHead->vpValue; // multi-valued
|
|
pAttrVals[i].bBerValue = false;
|
|
pAttrVals[i].Next = pHead->Next;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (i_pszManagedBy && *i_pszManagedBy)
|
|
{
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_MANAGEDBY;
|
|
pAttrVals[i].vpValue = (void *)i_pszManagedBy;
|
|
pAttrVals[i].bBerValue = false;
|
|
i++;
|
|
}
|
|
|
|
if (i > 0)
|
|
{
|
|
hr = ModifyValues(i_pldap, i_pszDN, i, pAttrVals);
|
|
dfsDebugOut((_T("ModifyValues i=%d, hr=%x\n"), i, hr));
|
|
}
|
|
|
|
if (pHead)
|
|
FreeAttrValList(pHead);
|
|
|
|
RETURN_IF_FAILED(hr);
|
|
|
|
//
|
|
// delete values if any
|
|
//
|
|
i =0;
|
|
ZeroMemory(pAttrVals, sizeof(pAttrVals));
|
|
if (!i_pszUNCPath || !*i_pszUNCPath)
|
|
{
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_UNCNAME;
|
|
pAttrVals[i].vpValue = NULL;
|
|
pAttrVals[i].bBerValue = false;
|
|
i++;
|
|
}
|
|
|
|
if (!i_pszDescription || !*i_pszDescription)
|
|
{
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_DESCRIPTION;
|
|
pAttrVals[i].vpValue = NULL;
|
|
pAttrVals[i].bBerValue = false;
|
|
i++;
|
|
}
|
|
|
|
if (!i_pszKeywords || !*i_pszKeywords)
|
|
{
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_KEYWORDS;
|
|
pAttrVals[i].vpValue = NULL;
|
|
pAttrVals[i].bBerValue = false;
|
|
i++;
|
|
}
|
|
|
|
if (!i_pszManagedBy || !*i_pszManagedBy)
|
|
{
|
|
pAttrVals[i].bstrAttribute = ATTR_SHRPUB_MANAGEDBY;
|
|
pAttrVals[i].vpValue = NULL;
|
|
pAttrVals[i].bBerValue = false;
|
|
i++;
|
|
}
|
|
|
|
if (i > 0)
|
|
{
|
|
hr = DeleteValues(i_pldap, i_pszDN, i, pAttrVals);
|
|
dfsDebugOut((_T("DeleteValues i=%d, hr=%x\n"), i, hr));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ModifySharePublishInfoOnFTRoot(
|
|
IN PCTSTR i_pszDomainName,
|
|
IN PCTSTR i_pszRootName,
|
|
IN BOOL i_bPublish,
|
|
IN PCTSTR i_pszUNCPath,
|
|
IN PCTSTR i_pszDescription,
|
|
IN PCTSTR i_pszKeywords,
|
|
IN PCTSTR i_pszManagedBy
|
|
)
|
|
{
|
|
dfsDebugOut((_T("ModifySharePublishInfoOnFTRoot %s, %s, %d, %s, %s, %s, %s\n"),
|
|
i_pszDomainName,
|
|
i_pszRootName,
|
|
i_bPublish,
|
|
i_pszUNCPath,
|
|
i_pszDescription,
|
|
i_pszKeywords,
|
|
i_pszManagedBy
|
|
));
|
|
|
|
CComBSTR bstrFTDfsObjectDN;
|
|
HRESULT hr = GetFTDfsObjectDN(i_pszDomainName, i_pszRootName, &bstrFTDfsObjectDN);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
CComBSTR bstrDC;
|
|
PLDAP pldap = NULL;
|
|
hr = ConnectToDS(i_pszDomainName, &pldap, &bstrDC); // PDC is preferred
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (i_bPublish)
|
|
{
|
|
CComBSTR bstrManagedByFQDN;
|
|
if (i_pszManagedBy && *i_pszManagedBy)
|
|
{
|
|
hr = TranslateManagedBy(bstrDC,
|
|
i_pszManagedBy,
|
|
&bstrManagedByFQDN,
|
|
(_tcschr(i_pszManagedBy, _T('@')) ? DS_USER_PRINCIPAL_NAME : DS_NT4_ACCOUNT_NAME),
|
|
DS_FQDN_1779_NAME);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = ModifyShareObject(
|
|
pldap,
|
|
bstrFTDfsObjectDN,
|
|
i_pszUNCPath,
|
|
i_pszDescription,
|
|
i_pszKeywords,
|
|
bstrManagedByFQDN);
|
|
} else {
|
|
hr = ModifyShareObject(
|
|
pldap,
|
|
bstrFTDfsObjectDN,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if (S_FALSE == hr)
|
|
hr = S_OK; // ignore non-existing object
|
|
}
|
|
|
|
CloseConnectionToDS(pldap);
|
|
}
|
|
|
|
dfsDebugOut((_T("ModifySharePublishInfoOnFTRoot hr=%x\n"), hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ModifySharePublishInfoOnSARoot(
|
|
IN PCTSTR i_pszServerName,
|
|
IN PCTSTR i_pszShareName,
|
|
IN BOOL i_bPublish,
|
|
IN PCTSTR i_pszUNCPath,
|
|
IN PCTSTR i_pszDescription,
|
|
IN PCTSTR i_pszKeywords,
|
|
IN PCTSTR i_pszManagedBy
|
|
)
|
|
{
|
|
dfsDebugOut((_T("ModifySharePublishInfoOnSARoot %s, %s, %d, %s, %s, %s, %s\n"),
|
|
i_pszServerName,
|
|
i_pszShareName,
|
|
i_bPublish,
|
|
i_pszUNCPath,
|
|
i_pszDescription,
|
|
i_pszKeywords,
|
|
i_pszManagedBy
|
|
));
|
|
|
|
CComBSTR bstrDomainName, bstrFQDN;
|
|
HRESULT hr = GetServerInfo(
|
|
(PTSTR)i_pszServerName,
|
|
&bstrDomainName,
|
|
NULL, //NetbiosName
|
|
NULL, //ValidDSObject
|
|
NULL, //DnsName,
|
|
NULL, //Guid,
|
|
&bstrFQDN);
|
|
if (S_OK != hr)
|
|
return hr;
|
|
|
|
CComBSTR bstrVolumeObjectDN = _T("CN=");
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrVolumeObjectDN);
|
|
bstrVolumeObjectDN += i_pszShareName;
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrVolumeObjectDN);
|
|
bstrVolumeObjectDN += _T(",");
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrVolumeObjectDN);
|
|
bstrVolumeObjectDN += bstrFQDN;
|
|
RETURN_OUTOFMEMORY_IF_NULL((BSTR)bstrVolumeObjectDN);
|
|
|
|
CComBSTR bstrDC;
|
|
PLDAP pldap = NULL;
|
|
hr = ConnectToDS(bstrDomainName, &pldap, &bstrDC);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (i_bPublish)
|
|
{
|
|
CComBSTR bstrManagedByFQDN;
|
|
if (i_pszManagedBy && *i_pszManagedBy)
|
|
{
|
|
hr = TranslateManagedBy(bstrDC,
|
|
i_pszManagedBy,
|
|
&bstrManagedByFQDN,
|
|
(_tcschr(i_pszManagedBy, _T('@')) ? DS_USER_PRINCIPAL_NAME : DS_NT4_ACCOUNT_NAME),
|
|
DS_FQDN_1779_NAME);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IsValidObject(pldap, bstrVolumeObjectDN);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = ModifyShareObject(
|
|
pldap,
|
|
bstrVolumeObjectDN,
|
|
i_pszUNCPath,
|
|
i_pszDescription,
|
|
i_pszKeywords,
|
|
bstrManagedByFQDN);
|
|
} else
|
|
{
|
|
hr = CreateVolumeObject(
|
|
pldap,
|
|
bstrVolumeObjectDN,
|
|
i_pszUNCPath,
|
|
i_pszDescription,
|
|
i_pszKeywords,
|
|
bstrManagedByFQDN);
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
hr = DeleteDSObject(pldap, bstrVolumeObjectDN, TRUE);
|
|
if (S_FALSE == hr)
|
|
hr = S_OK; // ignore non-existing object
|
|
}
|
|
|
|
CloseConnectionToDS(pldap);
|
|
}
|
|
|
|
dfsDebugOut((_T("ModifySharePublishInfoOnSARoot hr=%x\n"), hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT PutMultiValuesIntoAttrValList(
|
|
IN PCTSTR i_pszValues,
|
|
OUT LDAP_ATTR_VALUE** o_pVal
|
|
)
|
|
{
|
|
if (!i_pszValues || !o_pVal)
|
|
return E_INVALIDARG;
|
|
|
|
LDAP_ATTR_VALUE* pHead = NULL;
|
|
LDAP_ATTR_VALUE* pCurrent = NULL;
|
|
|
|
int index = 0;
|
|
CComBSTR bstrToken;
|
|
HRESULT hr = mystrtok(i_pszValues, &index, _T(";"), &bstrToken);
|
|
while (SUCCEEDED(hr) && (BSTR)bstrToken)
|
|
{
|
|
TrimBSTR(bstrToken);
|
|
|
|
if (*bstrToken)
|
|
{
|
|
LDAP_ATTR_VALUE* pNew = new LDAP_ATTR_VALUE;
|
|
RETURN_OUTOFMEMORY_IF_NULL(pNew);
|
|
pNew->vpValue = _tcsdup(bstrToken);
|
|
if (!(pNew->vpValue))
|
|
{
|
|
delete pNew;
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
if (!pHead)
|
|
{
|
|
pHead = pCurrent = pNew;
|
|
} else
|
|
{
|
|
pCurrent->Next = pNew;
|
|
pCurrent = pNew;
|
|
}
|
|
}
|
|
|
|
bstrToken.Empty();
|
|
hr = mystrtok(i_pszValues, &index, _T(";"), &bstrToken);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
FreeAttrValList(pHead);
|
|
return hr;
|
|
}
|
|
|
|
int nCount = 0;
|
|
pCurrent = pHead;
|
|
while (pCurrent)
|
|
{
|
|
nCount++;
|
|
pCurrent = pCurrent->Next;
|
|
}
|
|
if (!nCount)
|
|
return S_FALSE; // no token
|
|
|
|
*o_pVal = pHead;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT PutMultiValuesIntoStringArray(
|
|
IN PCTSTR i_pszValues,
|
|
OUT PTSTR** o_pVal
|
|
)
|
|
{
|
|
if (!i_pszValues || !o_pVal)
|
|
return E_INVALIDARG;
|
|
|
|
int nCount = 0;
|
|
CComBSTR bstrToken;
|
|
int index = 0;
|
|
HRESULT hr = mystrtok(i_pszValues, &index, _T(";"), &bstrToken);
|
|
while (SUCCEEDED(hr) && (BSTR)bstrToken)
|
|
{
|
|
nCount++;
|
|
|
|
bstrToken.Empty();
|
|
hr = mystrtok(i_pszValues, &index, _T(";"), &bstrToken);;
|
|
}
|
|
|
|
if (!nCount)
|
|
return E_INVALIDARG;
|
|
|
|
PTSTR* ppszStrings = (PTSTR *)calloc(nCount + 1, sizeof(PTSTR *));
|
|
RETURN_OUTOFMEMORY_IF_NULL(ppszStrings);
|
|
|
|
nCount = 0;
|
|
index = 0;
|
|
bstrToken.Empty();
|
|
hr = mystrtok(i_pszValues, &index, _T(";"), &bstrToken);
|
|
while (SUCCEEDED(hr) && (BSTR)bstrToken)
|
|
{
|
|
TrimBSTR(bstrToken);
|
|
if (*bstrToken)
|
|
{
|
|
ppszStrings[nCount] = _tcsdup(bstrToken);
|
|
BREAK_OUTOFMEMORY_IF_NULL(ppszStrings[nCount], &hr);
|
|
|
|
nCount++;
|
|
}
|
|
|
|
bstrToken.Empty();
|
|
hr = mystrtok(i_pszValues, &index, _T(";"), &bstrToken);;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
FreeStringArray(ppszStrings);
|
|
else
|
|
*o_pVal = ppszStrings;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// free a null-terminated array of strings
|
|
//
|
|
void FreeStringArray(PTSTR* i_ppszStrings)
|
|
{
|
|
if (i_ppszStrings)
|
|
{
|
|
PTSTR* ppszString = i_ppszStrings;
|
|
while (*ppszString)
|
|
{
|
|
free(*ppszString);
|
|
ppszString++;
|
|
}
|
|
|
|
free(i_ppszStrings);
|
|
}
|
|
}
|
|
|
|
HRESULT mystrtok(
|
|
IN PCTSTR i_pszString,
|
|
IN OUT int* io_pnIndex, // start from 0
|
|
IN PCTSTR i_pszCharSet,
|
|
OUT BSTR* o_pbstrToken
|
|
)
|
|
{
|
|
if (!i_pszString || !*i_pszString ||
|
|
!i_pszCharSet || !io_pnIndex ||
|
|
!o_pbstrToken)
|
|
return E_INVALIDARG;
|
|
|
|
*o_pbstrToken = NULL;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if (*io_pnIndex >= lstrlen(i_pszString))
|
|
{
|
|
return hr; // no more tokens
|
|
}
|
|
|
|
TCHAR *ptchStart = (PTSTR)i_pszString + *io_pnIndex;
|
|
if (!*i_pszCharSet)
|
|
{
|
|
*o_pbstrToken = SysAllocString(ptchStart);
|
|
if (!*o_pbstrToken)
|
|
hr = E_OUTOFMEMORY;
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// move p to the 1st char of the token
|
|
//
|
|
TCHAR *p = ptchStart;
|
|
while (*p)
|
|
{
|
|
if (_tcschr(i_pszCharSet, *p))
|
|
p++;
|
|
else
|
|
break;
|
|
}
|
|
|
|
ptchStart = p; // adjust ptchStart to point at the 1st char of the token
|
|
|
|
//
|
|
// move p to the char after the last char of the token
|
|
//
|
|
while (*p)
|
|
{
|
|
if (_tcschr(i_pszCharSet, *p))
|
|
break;
|
|
else
|
|
p++;
|
|
}
|
|
|
|
//
|
|
// ptchStart: points at the 1st char of the token
|
|
// p: points at the char after the last char of the token
|
|
//
|
|
if (ptchStart != p)
|
|
{
|
|
*o_pbstrToken = SysAllocStringLen(ptchStart, (int)(p - ptchStart));
|
|
if (!*o_pbstrToken)
|
|
hr = E_OUTOFMEMORY;
|
|
*io_pnIndex = (int)(p - i_pszString);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// trim off space chars at the beginning and at the end of the string
|
|
//
|
|
void TrimBSTR(BSTR bstr)
|
|
{
|
|
if (!bstr)
|
|
return;
|
|
|
|
TCHAR* p = bstr;
|
|
|
|
//
|
|
// trim off space chars at the beginning
|
|
//
|
|
while (*p)
|
|
{
|
|
if (_istspace(*p))
|
|
p++;
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (p > bstr)
|
|
_tcscpy(bstr, p);
|
|
|
|
int len = _tcslen(bstr);
|
|
if (len > 0)
|
|
{
|
|
//
|
|
// trim off space chars at the end
|
|
//
|
|
p = bstr + len - 1; // the char before the ending '\0'
|
|
while (p > bstr)
|
|
{
|
|
if (_istspace(*p))
|
|
p--;
|
|
else
|
|
{
|
|
*(p+1) = _T('\0');
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
BOOL CheckPolicyOnSharePublish()
|
|
{
|
|
//
|
|
// check group policy
|
|
//
|
|
BOOL bAddPublishPage = TRUE; // by default, we display the share publish page
|
|
|
|
HKEY hKey = NULL;
|
|
DWORD dwType = 0;
|
|
DWORD dwData = 0;
|
|
DWORD cbData = sizeof(dwData);
|
|
LONG lErr = RegOpenKeyEx(
|
|
HKEY_CURRENT_USER,
|
|
_T("Software\\Policies\\Microsoft\\Windows NT\\SharedFolders"),
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (ERROR_SUCCESS == lErr)
|
|
{
|
|
lErr = RegQueryValueEx(hKey, _T("PublishDfsRoots"), 0, &dwType, (LPBYTE)&dwData, &cbData);
|
|
|
|
if (ERROR_SUCCESS == lErr &&
|
|
REG_DWORD == dwType &&
|
|
0 == dwData) // policy is disabled
|
|
bAddPublishPage = FALSE;
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return bAddPublishPage;
|
|
|
|
}
|
|
|
|
BOOL CheckPolicyOnDisplayingInitialMaster()
|
|
{
|
|
BOOL bShowInitialMaster = FALSE; // by default, we hide the initial master on property page
|
|
|
|
HKEY hKey = NULL;
|
|
DWORD dwType = 0;
|
|
DWORD dwData = 0;
|
|
DWORD cbData = sizeof(dwData);
|
|
LONG lErr = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
_T("Software\\Microsoft\\DfsGui"),
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (ERROR_SUCCESS == lErr)
|
|
{
|
|
lErr = RegQueryValueEx(hKey, _T("ShowInitialMaster"), 0, &dwType, (LPBYTE)&dwData, &cbData);
|
|
|
|
if (ERROR_SUCCESS == lErr &&
|
|
REG_DWORD == dwType &&
|
|
1 == dwData)
|
|
bShowInitialMaster = TRUE;
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return bShowInitialMaster;
|
|
|
|
}
|
|
|
|
HRESULT GetMenuResourceStrings(
|
|
IN int i_iStringID,
|
|
OUT BSTR* o_pbstrMenuText,
|
|
OUT BSTR* o_pbstrToolTipText,
|
|
OUT BSTR* o_pbstrStatusBarText
|
|
)
|
|
{
|
|
if (!i_iStringID)
|
|
return E_INVALIDARG;
|
|
|
|
if (o_pbstrMenuText)
|
|
*o_pbstrMenuText = NULL;
|
|
if (o_pbstrToolTipText)
|
|
*o_pbstrToolTipText = NULL;
|
|
if (o_pbstrStatusBarText)
|
|
*o_pbstrStatusBarText = NULL;
|
|
|
|
TCHAR *pszMenuText = NULL;
|
|
TCHAR *pszToolTipText = NULL;
|
|
TCHAR *pszStatusBarText = NULL;
|
|
TCHAR *p = NULL;
|
|
|
|
CComBSTR bstr;
|
|
HRESULT hr = LoadStringFromResource(i_iStringID, &bstr);
|
|
RETURN_IF_FAILED(hr);
|
|
|
|
pszMenuText = (BSTR)bstr;
|
|
p = _tcschr(pszMenuText, _T('|'));
|
|
RETURN_INVALIDARG_IF_NULL(p);
|
|
*p++ = _T('\0');
|
|
|
|
pszToolTipText = p;
|
|
p = _tcschr(pszToolTipText, _T('|'));
|
|
RETURN_INVALIDARG_IF_NULL(p);
|
|
*p++ = _T('\0');
|
|
|
|
pszStatusBarText = p;
|
|
|
|
do {
|
|
if (o_pbstrMenuText)
|
|
{
|
|
*o_pbstrMenuText = SysAllocString(pszMenuText);
|
|
BREAK_OUTOFMEMORY_IF_NULL(*o_pbstrMenuText, &hr);
|
|
}
|
|
|
|
if (o_pbstrToolTipText)
|
|
{
|
|
*o_pbstrToolTipText = SysAllocString(pszToolTipText);
|
|
BREAK_OUTOFMEMORY_IF_NULL(*o_pbstrToolTipText, &hr);
|
|
}
|
|
|
|
if (o_pbstrStatusBarText)
|
|
{
|
|
*o_pbstrStatusBarText = SysAllocString(pszStatusBarText);
|
|
BREAK_OUTOFMEMORY_IF_NULL(*o_pbstrStatusBarText, &hr);
|
|
}
|
|
} while (0);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (o_pbstrMenuText && *o_pbstrMenuText)
|
|
SysFreeString(*o_pbstrMenuText);
|
|
if (o_pbstrToolTipText && *o_pbstrToolTipText)
|
|
SysFreeString(*o_pbstrToolTipText);
|
|
if (o_pbstrStatusBarText && *o_pbstrStatusBarText)
|
|
SysFreeString(*o_pbstrStatusBarText);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
WNDPROC g_fnOldEditCtrlProc;
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: NoPasteEditCtrlProc
|
|
//
|
|
// Synopsis: The subclassed edit control callback procedure.
|
|
// The paste of this edit control is disabled.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
LRESULT CALLBACK
|
|
NoPasteEditCtrlProc(
|
|
HWND hwnd,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
if (WM_PASTE == uMsg)
|
|
{
|
|
::MessageBeep (0);
|
|
return TRUE;
|
|
}
|
|
|
|
return CallWindowProc(g_fnOldEditCtrlProc, hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
void SetActivePropertyPage(IN HWND i_hwndParent, IN HWND i_hwndPage)
|
|
{
|
|
int index = ::SendMessage(i_hwndParent, PSM_HWNDTOINDEX, (WPARAM)i_hwndPage, 0);
|
|
if (-1 != index)
|
|
::SendMessage(i_hwndParent, PSM_SETCURSEL, (WPARAM)index, 0);
|
|
}
|
|
|
|
void MyShowWindow(HWND hwnd, BOOL bShow)
|
|
{
|
|
::ShowWindow(hwnd, (bShow ? SW_NORMAL : SW_HIDE));
|
|
::EnableWindow(hwnd, (bShow ? TRUE : FALSE));
|
|
}
|
|
|
|
//
|
|
// 7/11/2001 LinanT bug#426953
|
|
// Since connection made by Terminal Service may bring some client side resources
|
|
// (disks, serial ports, etc.) into "My Computer" namespace, we want to disable
|
|
// the OK button when browsing to a non-local folder. We don't have this problem
|
|
// when browsing a remote machine.
|
|
//
|
|
typedef struct _LOCAL_DISKS
|
|
{
|
|
LPTSTR pszDisks;
|
|
DWORD dwNumOfDisks;
|
|
} LOCAL_DISKS;
|
|
|
|
#define DISK_ENTRY_LENGTH 3 // Drive letter, colon, NULL
|
|
#define DISK_NAME_LENGTH 2 // Drive letter, colon
|
|
|
|
BOOL InDiskList(IN LPCTSTR pszDir, IN LOCAL_DISKS *pDisks)
|
|
{
|
|
if (!pszDir || !pDisks)
|
|
return FALSE;
|
|
|
|
DWORD i = 0;
|
|
PTSTR pszDisk = pDisks->pszDisks;
|
|
for (; pszDisk && i < pDisks->dwNumOfDisks; i++)
|
|
{
|
|
if (!_tcsnicmp(pszDisk, pszDir, DISK_NAME_LENGTH))
|
|
return TRUE;
|
|
|
|
pszDisk += DISK_ENTRY_LENGTH;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int CALLBACK
|
|
BrowseCallbackProc(
|
|
IN HWND hwnd,
|
|
IN UINT uMsg,
|
|
IN LPARAM lp,
|
|
IN LPARAM pData
|
|
)
|
|
{
|
|
switch(uMsg) {
|
|
case BFFM_SELCHANGED:
|
|
{
|
|
// enable the OK button if the selected path is local to that computer.
|
|
BOOL bEnableOK = FALSE;
|
|
TCHAR szDir[MAX_PATH];
|
|
if (SHGetPathFromIDList((LPITEMIDLIST) lp ,szDir))
|
|
{
|
|
if (pData)
|
|
{
|
|
// we're looking at a local computer, verify if szDir is on a local disk
|
|
bEnableOK = InDiskList(szDir, (LOCAL_DISKS *)pData);
|
|
} else
|
|
{
|
|
// no such problem when browsing at a remote computer, always enable OK button.
|
|
bEnableOK = TRUE;
|
|
}
|
|
}
|
|
SendMessage(hwnd, BFFM_ENABLEOK, 0, (LPARAM)bEnableOK);
|
|
break;
|
|
}
|
|
case BFFM_VALIDATEFAILED:
|
|
{
|
|
DisplayMessageBox(hwnd, MB_OK, 0, IDS_BROWSE_FOLDER_INVALID);
|
|
return 1;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void OpenBrowseDialog(
|
|
IN HWND hwndParent,
|
|
IN int idLabel,
|
|
IN BOOL bLocalComputer,
|
|
IN LPCTSTR lpszComputer,
|
|
OUT LPTSTR lpszDir)
|
|
{
|
|
_ASSERT(lpszComputer && *lpszComputer);
|
|
|
|
LOCAL_DISKS localDisks = {0};
|
|
|
|
CComBSTR bstrComputer;
|
|
if (*lpszComputer != _T('\\') || *(lpszComputer + 1) != _T('\\'))
|
|
{
|
|
bstrComputer = _T("\\\\");
|
|
bstrComputer += lpszComputer;
|
|
} else
|
|
{
|
|
bstrComputer = lpszComputer;
|
|
}
|
|
|
|
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPMALLOC pMalloc;
|
|
hr = SHGetMalloc(&pMalloc);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPSHELLFOLDER pDesktopFolder;
|
|
hr = SHGetDesktopFolder(&pDesktopFolder);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPITEMIDLIST pidlRoot;
|
|
if (bLocalComputer)
|
|
{
|
|
hr = SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidlRoot);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// 7/11/2001 LinanT bug#426953
|
|
// Since connection made by Terminal Service may bring some client side resources
|
|
// (disks, serial ports, etc.) into "My Computer" namespace, we want to disable
|
|
// the OK button when browsing to a non-local folder. We don't have this problem
|
|
// when browsing a remote machine.
|
|
//
|
|
//
|
|
// Get an array of local disk names, this information is later used
|
|
// in the browse dialog to disable OK button if non-local path is selected.
|
|
//
|
|
DWORD dwTotalEntries = 0;
|
|
DWORD nStatus = NetServerDiskEnum(
|
|
NULL, // local computer
|
|
0, // level must be zero
|
|
(LPBYTE *)&(localDisks.pszDisks),
|
|
-1, // dwPrefMaxLen,
|
|
&(localDisks.dwNumOfDisks),
|
|
&dwTotalEntries,
|
|
NULL);
|
|
if (NERR_Success != nStatus)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(nStatus);
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
hr = pDesktopFolder->ParseDisplayName(hwndParent, NULL, bstrComputer, NULL, &pidlRoot, NULL);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CComBSTR bstrLabel;
|
|
LoadStringFromResource(idLabel, &bstrLabel);
|
|
|
|
BROWSEINFO bi;
|
|
ZeroMemory(&bi,sizeof(bi));
|
|
bi.hwndOwner = hwndParent;
|
|
bi.pszDisplayName = 0;
|
|
bi.lpszTitle = bstrLabel;
|
|
bi.pidlRoot = pidlRoot;
|
|
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_SHAREABLE | BIF_USENEWUI | BIF_VALIDATE;
|
|
bi.lpfn = BrowseCallbackProc;
|
|
if (localDisks.pszDisks)
|
|
bi.lParam = (LPARAM)&localDisks; // pass the structure to the browse dialog
|
|
|
|
LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
|
|
if (pidl) {
|
|
SHGetPathFromIDList(pidl, lpszDir);
|
|
pMalloc->Free(pidl);
|
|
}
|
|
pMalloc->Free(pidlRoot);
|
|
}
|
|
pDesktopFolder->Release();
|
|
}
|
|
pMalloc->Release();
|
|
}
|
|
|
|
CoUninitialize();
|
|
}
|
|
|
|
if (localDisks.pszDisks)
|
|
NetApiBufferFree(localDisks.pszDisks);
|
|
|
|
if (FAILED(hr))
|
|
DisplayMessageBox(::GetActiveWindow(), MB_OK, hr, IDS_CANNOT_BROWSE_FOLDER, lpszComputer);
|
|
}
|