525 lines
17 KiB
C++
525 lines
17 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 1990-1998, Microsoft Corporation All rights reserved.
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
util.cpp
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module implements utility functions for the common dialog.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Arul Kumaravel (arulk@microsoft.com)
|
||
|
|
||
|
History:
|
||
|
|
||
|
Mar-07-2001 - Lazar Ivanov (LazarI)
|
||
|
reimplemented ThunkDevNamesW2A & ThunkDevNamesA2W
|
||
|
|
||
|
--*/
|
||
|
|
||
|
// precompiled headers
|
||
|
#include "precomp.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "cdids.h"
|
||
|
#include "fileopen.h"
|
||
|
#include "filenew.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
// crtfree.h is located in shell\inc and it defines new and delete
|
||
|
// operators to do LocalAlloc and LocalFree, so you don't have to
|
||
|
// link to MSVCRT in order to get those. i tried to remove this code
|
||
|
// and link to MSVCRT, but there are some ugly written code here
|
||
|
// which relies on the new operator to zero initialize the returned
|
||
|
// memory block so the class don't bother to initialize its members
|
||
|
// in the constructor. as i said this is quite ugly, but nothing i can
|
||
|
// do about this at the moment.
|
||
|
//
|
||
|
// LazarI - 2/21/2001
|
||
|
//
|
||
|
#define DECL_CRTFREE
|
||
|
#include <crtfree.h>
|
||
|
|
||
|
#ifndef ASSERT
|
||
|
#define ASSERT Assert
|
||
|
#endif
|
||
|
|
||
|
#define EVAL(x) x
|
||
|
|
||
|
#define USE_AUTOCOMPETE_DEFAULT TRUE
|
||
|
#define SZ_REGKEY_USEAUTOCOMPLETE TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoComplete")
|
||
|
#define SZ_REGVALUE_FILEDLGAUTOCOMPLETE TEXT("AutoComplete In File Dialog")
|
||
|
#define BOOL_NOT_SET 0x00000005
|
||
|
#define SZ_REGVALUE_AUTOCOMPLETE_TAB TEXT("Always Use Tab")
|
||
|
|
||
|
/****************************************************\
|
||
|
FUNCTION: AutoComplete
|
||
|
|
||
|
DESCRIPTION:
|
||
|
This function will have AutoComplete take over
|
||
|
an editbox to help autocomplete DOS paths.
|
||
|
\****************************************************/
|
||
|
HRESULT AutoComplete(HWND hwndEdit, ICurrentWorkingDirectory ** ppcwd, DWORD dwFlags)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
IUnknown * punkACLISF;
|
||
|
static BOOL fUseAutoComplete = -10; // Not inited.
|
||
|
|
||
|
if (-10 == fUseAutoComplete)
|
||
|
fUseAutoComplete = (SHRegGetBoolUSValue(SZ_REGKEY_USEAUTOCOMPLETE, SZ_REGVALUE_FILEDLGAUTOCOMPLETE, FALSE, USE_AUTOCOMPETE_DEFAULT));
|
||
|
|
||
|
// WARNING: If you want to disable AutoComplete by default,
|
||
|
// turn USE_AUTOCOMPETE_DEFAULT to FALSE
|
||
|
if (fUseAutoComplete)
|
||
|
{
|
||
|
Assert(!dwFlags); // Not yet used.
|
||
|
hr = SHCoCreateInstance(NULL, &CLSID_ACListISF, NULL, IID_PPV_ARG(IUnknown, &punkACLISF));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
IAutoComplete2 * pac;
|
||
|
// Create the AutoComplete Object
|
||
|
hr = SHCoCreateInstance(NULL, &CLSID_AutoComplete, NULL, IID_PPV_ARG(IAutoComplete2, &pac));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
DWORD dwOptions = 0;
|
||
|
|
||
|
hr = pac->Init(hwndEdit, punkACLISF, NULL, NULL);
|
||
|
|
||
|
// Set the autocomplete options
|
||
|
if (SHRegGetBoolUSValue(REGSTR_PATH_AUTOCOMPLETE, REGSTR_VAL_USEAUTOAPPEND, FALSE, /*default:*/FALSE))
|
||
|
{
|
||
|
dwOptions |= ACO_AUTOAPPEND;
|
||
|
}
|
||
|
|
||
|
if (SHRegGetBoolUSValue(REGSTR_PATH_AUTOCOMPLETE, REGSTR_VAL_USEAUTOSUGGEST, FALSE, /*default:*/TRUE))
|
||
|
{
|
||
|
dwOptions |= ACO_AUTOSUGGEST;
|
||
|
}
|
||
|
|
||
|
// Windows uses the TAB key to move between controls in a dialog. UNIX and other
|
||
|
// operating systems that use AutoComplete have traditionally used the TAB key to
|
||
|
// iterate thru the AutoComplete possibilities. We need to default to disable the
|
||
|
// TAB key (ACO_USETAB) unless the caller specifically wants it. We will also
|
||
|
// turn it on
|
||
|
static BOOL s_fAlwaysUseTab = BOOL_NOT_SET;
|
||
|
if (BOOL_NOT_SET == s_fAlwaysUseTab)
|
||
|
s_fAlwaysUseTab = SHRegGetBoolUSValue(SZ_REGKEY_USEAUTOCOMPLETE, SZ_REGVALUE_AUTOCOMPLETE_TAB, FALSE, FALSE);
|
||
|
|
||
|
if (s_fAlwaysUseTab)
|
||
|
dwOptions |= ACO_USETAB;
|
||
|
|
||
|
EVAL(SUCCEEDED(pac->SetOptions(dwOptions)));
|
||
|
|
||
|
pac->Release();
|
||
|
}
|
||
|
|
||
|
if (ppcwd)
|
||
|
{
|
||
|
punkACLISF->QueryInterface(IID_PPV_ARG(ICurrentWorkingDirectory, ppcwd));
|
||
|
}
|
||
|
|
||
|
punkACLISF->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Common Dialog Administrator Restrictions
|
||
|
//
|
||
|
////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
const SHRESTRICTIONITEMS c_rgRestItems[] =
|
||
|
{
|
||
|
{REST_NOBACKBUTTON, L"Comdlg32", L"NoBackButton"},
|
||
|
{REST_NOFILEMRU , L"Comdlg32", L"NoFileMru"},
|
||
|
{REST_NOPLACESBAR, L"Comdlg32", L"NoPlacesBar"},
|
||
|
{0, NULL, NULL},
|
||
|
};
|
||
|
|
||
|
#define NUMRESTRICTIONS ARRAYSIZE(c_rgRestItems)
|
||
|
|
||
|
|
||
|
DWORD g_rgRestItemValues[NUMRESTRICTIONS - 1 ] = { -1 };
|
||
|
|
||
|
DWORD IsRestricted(COMMDLG_RESTRICTIONS rest)
|
||
|
{
|
||
|
static BOOL bFirstTime = TRUE;
|
||
|
|
||
|
if (bFirstTime)
|
||
|
{
|
||
|
memset((LPBYTE)g_rgRestItemValues, (BYTE)-1, sizeof(g_rgRestItemValues));
|
||
|
bFirstTime = FALSE;
|
||
|
}
|
||
|
return SHRestrictionLookup(rest, NULL, c_rgRestItems, g_rgRestItemValues);
|
||
|
}
|
||
|
|
||
|
#define MODULE_NAME_SIZE 128
|
||
|
#define MODULE_VERSION_SIZE 15
|
||
|
|
||
|
typedef struct tagAPPCOMPAT
|
||
|
{
|
||
|
LPCTSTR pszModule;
|
||
|
LPCTSTR pszVersion;
|
||
|
DWORD dwFlags;
|
||
|
} APPCOMPAT, FAR* LPAPPCOMPAT;
|
||
|
|
||
|
DWORD CDGetAppCompatFlags()
|
||
|
{
|
||
|
static BOOL bInitialized = FALSE;
|
||
|
static DWORD dwCachedFlags = 0;
|
||
|
static const APPCOMPAT aAppCompat[] =
|
||
|
{ //Mathcad
|
||
|
{TEXT("MCAD.EXE"), TEXT("6.00b"), CDACF_MATHCAD},
|
||
|
//Picture Publisher
|
||
|
{TEXT("PP70.EXE"),NULL, CDACF_NT40TOOLBAR},
|
||
|
{TEXT("PP80.EXE"),NULL, CDACF_NT40TOOLBAR},
|
||
|
//Code Wright
|
||
|
{TEXT("CW32.exe"),TEXT("5.1"), CDACF_NT40TOOLBAR},
|
||
|
//Designer.exe
|
||
|
{TEXT("ds70.exe"),NULL, CDACF_FILETITLE}
|
||
|
};
|
||
|
|
||
|
if (!bInitialized)
|
||
|
{
|
||
|
TCHAR szModulePath[MODULE_NAME_SIZE];
|
||
|
TCHAR* pszModuleName;
|
||
|
DWORD dwHandle;
|
||
|
int i;
|
||
|
|
||
|
GetModuleFileName(GetModuleHandle(NULL), szModulePath, ARRAYSIZE(szModulePath));
|
||
|
pszModuleName = PathFindFileName(szModulePath);
|
||
|
|
||
|
if (pszModuleName)
|
||
|
{
|
||
|
for (i=0; i < ARRAYSIZE(aAppCompat); i++)
|
||
|
{
|
||
|
if (lstrcmpi(aAppCompat[i].pszModule, pszModuleName) == 0)
|
||
|
{
|
||
|
if (aAppCompat[i].pszVersion == NULL)
|
||
|
{
|
||
|
dwCachedFlags = aAppCompat[i].dwFlags;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CHAR chBuffer[3072]; // hopefully this is enough... lotus smart center needs 3000
|
||
|
TCHAR* pszVersion = NULL;
|
||
|
UINT cb;
|
||
|
|
||
|
// get module version here!
|
||
|
cb = GetFileVersionInfoSize(szModulePath, &dwHandle);
|
||
|
if (cb <= ARRAYSIZE(chBuffer) &&
|
||
|
GetFileVersionInfo(szModulePath, dwHandle, ARRAYSIZE(chBuffer), (LPVOID)chBuffer) &&
|
||
|
VerQueryValue((LPVOID)chBuffer, TEXT("\\StringFileInfo\\040904E4\\ProductVersion"), (void **) &pszVersion, &cb))
|
||
|
{
|
||
|
DebugMsg(0x0004, TEXT("product: %s\n version: %s"), pszModuleName, pszVersion);
|
||
|
if (lstrcmpi(pszVersion, aAppCompat[i].pszVersion) == 0)
|
||
|
{
|
||
|
dwCachedFlags = aAppCompat[i].dwFlags;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
bInitialized = TRUE;
|
||
|
}
|
||
|
|
||
|
return dwCachedFlags;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL ILIsFTP(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
IShellFolder * psf;
|
||
|
BOOL fIsFTPFolder = FALSE;
|
||
|
|
||
|
if (SUCCEEDED(CDBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidl, &psf))))
|
||
|
{
|
||
|
CLSID clsid;
|
||
|
|
||
|
if (SUCCEEDED(IUnknown_GetClassID(psf, &clsid)) &&
|
||
|
(IsEqualIID(clsid, CLSID_FtpFolder)))
|
||
|
{
|
||
|
fIsFTPFolder = TRUE;
|
||
|
}
|
||
|
|
||
|
psf->Release();
|
||
|
}
|
||
|
|
||
|
return fIsFTPFolder;
|
||
|
}
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
extern "C" {
|
||
|
#endif
|
||
|
|
||
|
// this is weak.
|
||
|
// a long time ago somebody changed all the FindResources to call FindResourceEx, specifying
|
||
|
// a language. thatd be cool except FindResource already has logic to get the right language.
|
||
|
// whatever was busted should have probably been fixed some other way.
|
||
|
// not only that but it's broken because MUI needs to fall back to US if it can't get the resource
|
||
|
// from the MUI language-specific files.
|
||
|
// thus force a fallback to US. really everything should be rewritten to be normal like every other
|
||
|
// DLL but there's a lot of weird TLS stuff that would break and its risky for this late in XP.
|
||
|
HRSRC FindResourceExFallback(HMODULE hModule, LPCTSTR lpType, LPCTSTR lpName, WORD wLanguage)
|
||
|
{
|
||
|
HRSRC hrsrc = FindResourceEx(hModule, lpType, lpName, wLanguage);
|
||
|
if (!hrsrc)
|
||
|
{
|
||
|
LANGID langid = GetSystemDefaultUILanguage();
|
||
|
if (langid && (langid != wLanguage))
|
||
|
{
|
||
|
hrsrc = FindResourceEx(hModule, lpType, lpName, langid);
|
||
|
}
|
||
|
}
|
||
|
return hrsrc;
|
||
|
}
|
||
|
|
||
|
// Win32Error2HRESULT: converts Win32 error to HRESULT
|
||
|
inline HRESULT Win32Error2HRESULT(DWORD dwError = GetLastError())
|
||
|
{
|
||
|
return (ERROR_SUCCESS == dwError) ? E_FAIL : HRESULT_FROM_WIN32(dwError);
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Name:
|
||
|
|
||
|
ThunkDevNamesA2W
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Converts ANSI DEVNAMES structure to UNICODE
|
||
|
on failure we don't release *phDevNamesW
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hDevNamesA - [in] handle to ANSI DEVNAMES
|
||
|
phDevNamesW - [in, out] handle to UNICODE DEVNAMES
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
S_OK if succeded and OLE error otherwise
|
||
|
|
||
|
History:
|
||
|
|
||
|
Lazar Ivanov (LazarI), Mar-07-2001 - created.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
HRESULT
|
||
|
ThunkDevNamesA2W(
|
||
|
IN HGLOBAL hDevNamesA,
|
||
|
IN OUT HGLOBAL *phDevNamesW
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
if (hDevNamesA && phDevNamesW)
|
||
|
{
|
||
|
LPDEVNAMES pDNA = (LPDEVNAMES )GlobalLock(hDevNamesA);
|
||
|
if (pDNA)
|
||
|
{
|
||
|
// calculate the input string pointers
|
||
|
LPSTR pszDriver = reinterpret_cast<LPSTR>(pDNA) + pDNA->wDriverOffset;
|
||
|
LPSTR pszDevice = reinterpret_cast<LPSTR>(pDNA) + pDNA->wDeviceOffset;
|
||
|
LPSTR pszOutput = reinterpret_cast<LPSTR>(pDNA) + pDNA->wOutputOffset;
|
||
|
|
||
|
// calculate the lengths of the ANSI strings
|
||
|
SIZE_T iDriverLenW = MultiByteToWideChar(CP_ACP, 0, pszDriver, -1, NULL, 0);
|
||
|
SIZE_T iDeviceLenW = MultiByteToWideChar(CP_ACP, 0, pszDevice, -1, NULL, 0);
|
||
|
SIZE_T iOutputLenW = MultiByteToWideChar(CP_ACP, 0, pszOutput, -1, NULL, 0);
|
||
|
|
||
|
// calculate the output buffer length
|
||
|
SIZE_T iBytesTotal = sizeof(DEVNAMES) + sizeof(WCHAR) *
|
||
|
((iDriverLenW + 1) + (iDeviceLenW + 1) + (iOutputLenW + 1) + DN_PADDINGCHARS);
|
||
|
|
||
|
HGLOBAL hDevNamesW = (*phDevNamesW) ?
|
||
|
GlobalReAlloc(*phDevNamesW, iBytesTotal, GHND) :
|
||
|
GlobalAlloc(GHND, iBytesTotal);
|
||
|
|
||
|
if (hDevNamesW)
|
||
|
{
|
||
|
// thunk DEVNAMES...
|
||
|
LPDEVNAMES pDNW = (LPDEVNAMES )GlobalLock(hDevNamesW);
|
||
|
if (pDNW)
|
||
|
{
|
||
|
// calculate the offsets
|
||
|
// note: the offsets are in chars not bytes!!
|
||
|
pDNW->wDriverOffset = sizeof(DEVNAMES) / sizeof(WCHAR);
|
||
|
pDNW->wDeviceOffset = pDNW->wDriverOffset + iDriverLenW + 1;
|
||
|
pDNW->wOutputOffset = pDNW->wDeviceOffset + iDeviceLenW + 1;
|
||
|
pDNW->wDefault = pDNA->wDefault;
|
||
|
|
||
|
// calculate the output string pointers
|
||
|
LPWSTR pwszDriver = reinterpret_cast<LPWSTR>(pDNW) + pDNW->wDriverOffset;
|
||
|
LPWSTR pwszDevice = reinterpret_cast<LPWSTR>(pDNW) + pDNW->wDeviceOffset;
|
||
|
LPWSTR pwszOutput = reinterpret_cast<LPWSTR>(pDNW) + pDNW->wOutputOffset;
|
||
|
|
||
|
// convert from ansi to uniciode
|
||
|
MultiByteToWideChar(CP_ACP, 0, pszDriver, -1, pwszDriver, iDriverLenW + 1);
|
||
|
MultiByteToWideChar(CP_ACP, 0, pszDevice, -1, pwszDevice, iDeviceLenW + 1);
|
||
|
MultiByteToWideChar(CP_ACP, 0, pszOutput, -1, pwszOutput, iOutputLenW + 1);
|
||
|
|
||
|
// unlock hDevNamesW
|
||
|
GlobalUnlock(hDevNamesW);
|
||
|
|
||
|
// declare success
|
||
|
*phDevNamesW = hDevNamesW;
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// GlobalLock failed
|
||
|
hr = Win32Error2HRESULT(GetLastError());
|
||
|
GlobalFree(hDevNamesW);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// GlobalAlloc failed
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
// unlock hDevNamesA
|
||
|
GlobalUnlock(hDevNamesA);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// GlobalLock failed
|
||
|
hr = Win32Error2HRESULT(GetLastError());
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// some of the arguments are invalid (NULL)
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Name:
|
||
|
|
||
|
ThunkDevNamesW2A
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Converts UNICODE DEVNAMES structure to ANSI
|
||
|
on failure we don't release *phDevNamesA
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hDevNamesW - [in] handle to UNICODE DEVNAMES
|
||
|
phDevNamesA - [in, out] handle to ANSI DEVNAMES
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
S_OK if succeded and OLE error otherwise
|
||
|
|
||
|
History:
|
||
|
|
||
|
Lazar Ivanov (LazarI), Mar-07-2001 - created.
|
||
|
|
||
|
--*/
|
||
|
HRESULT
|
||
|
ThunkDevNamesW2A(
|
||
|
IN HGLOBAL hDevNamesW,
|
||
|
IN OUT HGLOBAL *phDevNamesA
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
if (hDevNamesW && phDevNamesA)
|
||
|
{
|
||
|
LPDEVNAMES pDNW = (LPDEVNAMES)GlobalLock(hDevNamesW);
|
||
|
if (pDNW)
|
||
|
{
|
||
|
// calculate the input string pointers
|
||
|
LPWSTR pwszDriver = reinterpret_cast<LPWSTR>(pDNW) + pDNW->wDriverOffset;
|
||
|
LPWSTR pwszDevice = reinterpret_cast<LPWSTR>(pDNW) + pDNW->wDeviceOffset;
|
||
|
LPWSTR pwszOutput = reinterpret_cast<LPWSTR>(pDNW) + pDNW->wOutputOffset;
|
||
|
|
||
|
// calculate the lengths of the ANSI strings
|
||
|
SIZE_T iDriverLenA = WideCharToMultiByte(CP_ACP, 0, pwszDriver, -1, NULL, 0, NULL, NULL);
|
||
|
SIZE_T iDeviceLenA = WideCharToMultiByte(CP_ACP, 0, pwszDevice, -1, NULL, 0, NULL, NULL);
|
||
|
SIZE_T iOutputLenA = WideCharToMultiByte(CP_ACP, 0, pwszOutput, -1, NULL, 0, NULL, NULL);
|
||
|
|
||
|
// calculate the output buffer length
|
||
|
SIZE_T iBytesTotal = sizeof(DEVNAMES) + sizeof(CHAR) *
|
||
|
((iDriverLenA + 1) + (iDeviceLenA + 1) + (iOutputLenA + 1) + DN_PADDINGCHARS);
|
||
|
|
||
|
HGLOBAL hDevNamesA = (*phDevNamesA) ?
|
||
|
GlobalReAlloc(*phDevNamesA, iBytesTotal, GHND) :
|
||
|
GlobalAlloc(GHND, iBytesTotal);
|
||
|
if (hDevNamesA)
|
||
|
{
|
||
|
// thunk DEVNAMES...
|
||
|
LPDEVNAMES pDNA = (LPDEVNAMES )GlobalLock(hDevNamesA);
|
||
|
if (pDNA)
|
||
|
{
|
||
|
// calculate the offsets
|
||
|
// note: the offsets are in chars not bytes!!
|
||
|
pDNA->wDriverOffset = sizeof(DEVNAMES) / sizeof(CHAR);
|
||
|
pDNA->wDeviceOffset = pDNA->wDriverOffset + iDriverLenA + 1;
|
||
|
pDNA->wOutputOffset = pDNA->wDeviceOffset + iDeviceLenA + 1;
|
||
|
pDNA->wDefault = pDNW->wDefault;
|
||
|
|
||
|
// calculate the output string pointers
|
||
|
LPSTR pszDriver = reinterpret_cast<LPSTR>(pDNA) + pDNA->wDriverOffset;
|
||
|
LPSTR pszDevice = reinterpret_cast<LPSTR>(pDNA) + pDNA->wDeviceOffset;
|
||
|
LPSTR pszOutput = reinterpret_cast<LPSTR>(pDNA) + pDNA->wOutputOffset;
|
||
|
|
||
|
// convert from uniciode to ansi
|
||
|
WideCharToMultiByte(CP_ACP, 0, pwszDriver, -1, pszDriver, iDriverLenA + 1, NULL, NULL);
|
||
|
WideCharToMultiByte(CP_ACP, 0, pwszDevice, -1, pszDevice, iDeviceLenA + 1, NULL, NULL);
|
||
|
WideCharToMultiByte(CP_ACP, 0, pwszOutput, -1, pszOutput, iOutputLenA + 1, NULL, NULL);
|
||
|
|
||
|
// unlock hDevNamesA
|
||
|
GlobalUnlock(hDevNamesA);
|
||
|
|
||
|
// declare success
|
||
|
*phDevNamesA = hDevNamesA;
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// GlobalLock failed
|
||
|
hr = Win32Error2HRESULT(GetLastError());
|
||
|
GlobalFree(hDevNamesW);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// GlobalAlloc failed
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
// unlock hDevNamesW
|
||
|
GlobalUnlock(hDevNamesW);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// GlobalLock failed
|
||
|
hr = Win32Error2HRESULT(GetLastError());
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// some of the arguments are invalid (NULL)
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
}; // extern "C"
|
||
|
#endif
|