aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-08 14:06:21 -0700
committerRob Mensching <rob@firegiant.com>2021-04-12 12:29:11 -0700
commitb2c4600453f926fbfdc63219126930ad39601f25 (patch)
treeb7c8429ffcfe4f1f8b98f9e43b00d671b2d3a1d6 /src
parent0c2b4cf3a439eda3e19d20fadfc65ddc7d0394c0 (diff)
downloadwix-b2c4600453f926fbfdc63219126930ad39601f25.tar.gz
wix-b2c4600453f926fbfdc63219126930ad39601f25.tar.bz2
wix-b2c4600453f926fbfdc63219126930ad39601f25.zip
Port missing WiX v3 changes
Diffstat (limited to 'src')
-rw-r--r--src/dutil/dirutil.cpp31
-rw-r--r--src/dutil/inc/dirutil.h5
-rw-r--r--src/dutil/inc/locutil.h12
-rw-r--r--src/dutil/inc/pathutil.h8
-rw-r--r--src/dutil/locutil.cpp29
-rw-r--r--src/dutil/pathutil.cpp33
-rw-r--r--src/dutil/sqlutil.cpp171
7 files changed, 254 insertions, 35 deletions
diff --git a/src/dutil/dirutil.cpp b/src/dutil/dirutil.cpp
index 81130a8d..b10e71f3 100644
--- a/src/dutil/dirutil.cpp
+++ b/src/dutil/dirutil.cpp
@@ -353,6 +353,37 @@ LExit:
353 353
354 354
355/******************************************************************* 355/*******************************************************************
356DirDeleteEmptyDirectoriesToRoot - removes an empty directory and as many
357 of its parents as possible.
358
359 Returns: count of directories deleted.
360*******************************************************************/
361extern "C" DWORD DAPI DirDeleteEmptyDirectoriesToRoot(
362 __in_z LPCWSTR wzPath,
363 __in DWORD /*dwFlags*/
364 )
365{
366 DWORD cDeletedDirs = 0;
367 LPWSTR sczPath = NULL;
368
369 while (wzPath && *wzPath && ::RemoveDirectoryW(wzPath))
370 {
371 ++cDeletedDirs;
372
373 HRESULT hr = PathGetParentPath(wzPath, &sczPath);
374 DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", wzPath);
375
376 wzPath = sczPath;
377 }
378
379LExit:
380 ReleaseStr(sczPath);
381
382 return cDeletedDirs;
383}
384
385
386/*******************************************************************
356 DirGetCurrent - gets the current directory. 387 DirGetCurrent - gets the current directory.
357 388
358*******************************************************************/ 389*******************************************************************/
diff --git a/src/dutil/inc/dirutil.h b/src/dutil/inc/dirutil.h
index 0a19a9c0..539b3a73 100644
--- a/src/dutil/inc/dirutil.h
+++ b/src/dutil/inc/dirutil.h
@@ -40,6 +40,11 @@ HRESULT DAPI DirEnsureDeleteEx(
40 __in DWORD dwFlags 40 __in DWORD dwFlags
41 ); 41 );
42 42
43DWORD DAPI DirDeleteEmptyDirectoriesToRoot(
44 __in_z LPCWSTR wzPath,
45 __in DWORD dwFlags
46 );
47
43HRESULT DAPI DirGetCurrent( 48HRESULT DAPI DirGetCurrent(
44 __deref_out_z LPWSTR* psczCurrentDirectory 49 __deref_out_z LPWSTR* psczCurrentDirectory
45 ); 50 );
diff --git a/src/dutil/inc/locutil.h b/src/dutil/inc/locutil.h
index 38ddda20..626cb59e 100644
--- a/src/dutil/inc/locutil.h
+++ b/src/dutil/inc/locutil.h
@@ -50,6 +50,18 @@ HRESULT DAPI LocProbeForFile(
50 ); 50 );
51 51
52/******************************************************************** 52/********************************************************************
53 LocProbeForFileEx - Searches for a localization file on disk.
54 useUILanguage should be set to TRUE.
55*******************************************************************/
56HRESULT DAPI LocProbeForFileEx(
57 __in_z LPCWSTR wzBasePath,
58 __in_z LPCWSTR wzLocFileName,
59 __in_z_opt LPCWSTR wzLanguage,
60 __inout LPWSTR* psczPath,
61 __in BOOL fUseUILanguage
62 );
63
64/********************************************************************
53 LocLoadFromFile - Loads a localization file 65 LocLoadFromFile - Loads a localization file
54 66
55*******************************************************************/ 67*******************************************************************/
diff --git a/src/dutil/inc/pathutil.h b/src/dutil/inc/pathutil.h
index f4f4e59c..719ee7d8 100644
--- a/src/dutil/inc/pathutil.h
+++ b/src/dutil/inc/pathutil.h
@@ -47,6 +47,14 @@ DAPI_(HRESULT) PathGetDirectory(
47 ); 47 );
48 48
49/******************************************************************* 49/*******************************************************************
50PathGetParentPath - extracts the parent directory from a full path.
51********************************************************************/
52DAPI_(HRESULT) PathGetParentPath(
53 __in_z LPCWSTR wzPath,
54 __out_z LPWSTR *psczDirectory
55 );
56
57/*******************************************************************
50 PathExpand - gets the full path to a file resolving environment 58 PathExpand - gets the full path to a file resolving environment
51 variables along the way. 59 variables along the way.
52********************************************************************/ 60********************************************************************/
diff --git a/src/dutil/locutil.cpp b/src/dutil/locutil.cpp
index c4567c03..43e1bb5b 100644
--- a/src/dutil/locutil.cpp
+++ b/src/dutil/locutil.cpp
@@ -51,7 +51,7 @@ static HRESULT ParseWxlControl(
51#ifndef MUI_MERGE_SYSTEM_FALLBACK 51#ifndef MUI_MERGE_SYSTEM_FALLBACK
52#define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages 52#define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages
53#endif 53#endif
54typedef WINBASEAPI BOOL (WINAPI *GET_THREAD_PREFERRED_UI_LANGUAGES) ( 54typedef WINBASEAPI BOOL (WINAPI *PFN_GET_THREAD_PREFERRED_UI_LANGUAGES) (
55 __in DWORD dwFlags, 55 __in DWORD dwFlags,
56 __out PULONG pulNumLanguages, 56 __out PULONG pulNumLanguages,
57 __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer, 57 __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer,
@@ -65,14 +65,25 @@ extern "C" HRESULT DAPI LocProbeForFile(
65 __inout LPWSTR* psczPath 65 __inout LPWSTR* psczPath
66 ) 66 )
67{ 67{
68 return LocProbeForFileEx(wzBasePath, wzLocFileName, wzLanguage, psczPath, FALSE);
69}
70
71extern "C" HRESULT DAPI LocProbeForFileEx(
72 __in_z LPCWSTR wzBasePath,
73 __in_z LPCWSTR wzLocFileName,
74 __in_z_opt LPCWSTR wzLanguage,
75 __inout LPWSTR* psczPath,
76 __in BOOL fUseUILanguage
77 )
78{
68 HRESULT hr = S_OK; 79 HRESULT hr = S_OK;
69 LPWSTR sczProbePath = NULL; 80 LPWSTR sczProbePath = NULL;
70 LANGID langid = 0; 81 LANGID langid = 0;
71 LPWSTR sczLangIdFile = NULL; 82 LPWSTR sczLangIdFile = NULL;
72 LPWSTR sczLangsBuff = NULL; 83 LPWSTR sczLangsBuff = NULL;
73 GET_THREAD_PREFERRED_UI_LANGUAGES pvfnGetThreadPreferredUILanguages = 84 PFN_GET_THREAD_PREFERRED_UI_LANGUAGES pfnGetThreadPreferredUILanguages =
74 reinterpret_cast<GET_THREAD_PREFERRED_UI_LANGUAGES>( 85 reinterpret_cast<PFN_GET_THREAD_PREFERRED_UI_LANGUAGES>(
75 GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages")); 86 ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages"));
76 87
77 // If a language was specified, look for a loc file in that as a directory. 88 // If a language was specified, look for a loc file in that as a directory.
78 if (wzLanguage && *wzLanguage) 89 if (wzLanguage && *wzLanguage)
@@ -89,12 +100,12 @@ extern "C" HRESULT DAPI LocProbeForFile(
89 } 100 }
90 } 101 }
91 102
92 if (pvfnGetThreadPreferredUILanguages) 103 if (fUseUILanguage && pfnGetThreadPreferredUILanguages)
93 { 104 {
94 ULONG nLangs; 105 ULONG nLangs = 0;
95 ULONG cchLangs = 0; 106 ULONG cchLangs = 0;
96 DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK; 107 DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK;
97 if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs)) 108 if (!(*pfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs))
98 { 109 {
99 LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size."); 110 LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size.");
100 } 111 }
@@ -103,7 +114,7 @@ extern "C" HRESULT DAPI LocProbeForFile(
103 LocExitOnFailure(hr, "Failed to allocate buffer for languages"); 114 LocExitOnFailure(hr, "Failed to allocate buffer for languages");
104 115
105 nLangs = 0; 116 nLangs = 0;
106 if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs)) 117 if (!(*pfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs))
107 { 118 {
108 LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list."); 119 LocExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list.");
109 } 120 }
@@ -129,7 +140,7 @@ extern "C" HRESULT DAPI LocProbeForFile(
129 } 140 }
130 } 141 }
131 142
132 langid = ::GetUserDefaultUILanguage(); 143 langid = fUseUILanguage ? ::GetUserDefaultUILanguage() : ::GetUserDefaultLangID();
133 144
134 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName); 145 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
135 LocExitOnFailure(hr, "Failed to format user langid."); 146 LocExitOnFailure(hr, "Failed to format user langid.");
diff --git a/src/dutil/pathutil.cpp b/src/dutil/pathutil.cpp
index ec338f71..183849ac 100644
--- a/src/dutil/pathutil.cpp
+++ b/src/dutil/pathutil.cpp
@@ -215,6 +215,39 @@ LExit:
215} 215}
216 216
217 217
218DAPI_(HRESULT) PathGetParentPath(
219 __in_z LPCWSTR wzPath,
220 __out_z LPWSTR *psczParent
221 )
222{
223 HRESULT hr = S_OK;
224 LPCWSTR wzParent = NULL;
225
226 for (LPCWSTR wz = wzPath; *wz; ++wz)
227 {
228 if (wz[1] && (L'\\' == *wz || L'/' == *wz))
229 {
230 wzParent = wz;
231 }
232 }
233
234 if (wzParent)
235 {
236 DWORD cchPath = static_cast<DWORD>(wzParent - wzPath) + 1;
237
238 hr = StrAllocString(psczParent, wzPath, cchPath);
239 PathExitOnFailure(hr, "Failed to copy directory.");
240 }
241 else
242 {
243 ReleaseNullStr(psczParent);
244 }
245
246LExit:
247 return hr;
248}
249
250
218DAPI_(HRESULT) PathExpand( 251DAPI_(HRESULT) PathExpand(
219 __out LPWSTR *psczFullPath, 252 __out LPWSTR *psczFullPath,
220 __in_z LPCWSTR wzRelativePath, 253 __in_z LPCWSTR wzRelativePath,
diff --git a/src/dutil/sqlutil.cpp b/src/dutil/sqlutil.cpp
index 63ee80ac..782c7088 100644
--- a/src/dutil/sqlutil.cpp
+++ b/src/dutil/sqlutil.cpp
@@ -10,7 +10,22 @@
10#include "sqlutil.h" 10#include "sqlutil.h"
11 11
12 12
13//Please note that only SQL native client 11 has TLS1.2 support
14#define _SQLNCLI_OLEDB_DEPRECATE_WARNING
15
16#if !defined(SQLNCLI_VER)
17#define SQLNCLI_VER 1100
18#endif
19
20#if SQLNCLI_VER >= 1100
21#if defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_)
22#define SQLNCLI_CLSID CLSID_SQLNCLI11
23#endif // defined(_SQLNCLI_OLEDB_) || !defined(_SQLNCLI_ODBC_)
24extern const GUID OLEDBDECLSPEC _SQLNCLI_OLEDB_DEPRECATE_WARNING CLSID_SQLNCLI11 = { 0x397C2819L,0x8272,0x4532,{ 0xAD,0x3A,0xFB,0x5E,0x43,0xBE,0xAA,0x39 } };
25#endif // SQLNCLI_VER >= 1100
26
13// Exit macros 27// Exit macros
28#define SqlExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
14#define SqlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) 29#define SqlExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
15#define SqlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) 30#define SqlExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
16#define SqlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__) 31#define SqlExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SQLUTIL, x, s, __VA_ARGS__)
@@ -25,11 +40,18 @@
25#define SqlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SQLUTIL, g, x, s, __VA_ARGS__) 40#define SqlExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_SQLUTIL, g, x, s, __VA_ARGS__)
26 41
27// private prototypes 42// private prototypes
43static HRESULT InitializeDatabaseConnection(
44 __in REFCLSID rclsid,
45 __in_z LPCSTR szFriendlyClsidName,
46 __in DBPROPSET rgdbpsetInit[],
47 __in_ecount(rgdbpsetInit) DWORD cdbpsetInit,
48 __out IDBCreateSession** ppidbSession
49 );
50HRESULT DumpErrorRecords();
28static HRESULT FileSpecToString( 51static HRESULT FileSpecToString(
29 __in const SQL_FILESPEC* psf, 52 __in const SQL_FILESPEC* psf,
30 __out LPWSTR* ppwz 53 __out LPWSTR* ppwz
31 ); 54 );
32
33static HRESULT EscapeSqlIdentifier( 55static HRESULT EscapeSqlIdentifier(
34 __in_z LPCWSTR wzDatabase, 56 __in_z LPCWSTR wzDatabase,
35 __deref_out_z LPWSTR* ppwz 57 __deref_out_z LPWSTR* ppwz
@@ -55,22 +77,11 @@ extern "C" HRESULT DAPI SqlConnectDatabase(
55 Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession); 77 Assert(wzServer && wzDatabase && *wzDatabase && ppidbSession);
56 78
57 HRESULT hr = S_OK; 79 HRESULT hr = S_OK;
58 IDBInitialize* pidbInitialize = NULL;
59 IDBProperties* pidbProperties = NULL;
60
61 LPWSTR pwzServerInstance = NULL; 80 LPWSTR pwzServerInstance = NULL;
62 DBPROP rgdbpInit[4]; 81 DBPROP rgdbpInit[4] = { };
63 DBPROPSET rgdbpsetInit[1]; 82 DBPROPSET rgdbpsetInit[1] = { };
64 ULONG cProperties = 0; 83 ULONG cProperties = 0;
65 84
66 memset(rgdbpInit, 0, sizeof(rgdbpInit));
67 memset(rgdbpsetInit, 0, sizeof(rgdbpsetInit));
68
69 //obtain access to the SQLOLEDB provider
70 hr = ::CoCreateInstance(CLSID_SQLOLEDB, NULL, CLSCTX_INPROC_SERVER,
71 IID_IDBInitialize, (LPVOID*)&pidbInitialize);
72 SqlExitOnFailure(hr, "failed to create IID_IDBInitialize object");
73
74 // if there is an instance 85 // if there is an instance
75 if (wzInstance && *wzInstance) 86 if (wzInstance && *wzInstance)
76 { 87 {
@@ -137,17 +148,23 @@ extern "C" HRESULT DAPI SqlConnectDatabase(
137 rgdbpsetInit[0].rgProperties = rgdbpInit; 148 rgdbpsetInit[0].rgProperties = rgdbpInit;
138 rgdbpsetInit[0].cProperties = cProperties; 149 rgdbpsetInit[0].cProperties = cProperties;
139 150
140 // create and set the property set 151 // obtain access to the SQL Native Client provider
141 hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties); 152 hr = InitializeDatabaseConnection(SQLNCLI_CLSID, "SQL Native Client", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession);
142 SqlExitOnFailure(hr, "failed to get IID_IDBProperties object"); 153 if (FAILED(hr))
143 hr = pidbProperties->SetProperties(1, rgdbpsetInit); 154 {
144 SqlExitOnFailure(hr, "failed to set properties"); 155 SqlExitTrace(hr, "Could not initialize SQL Native Client, falling back to SQL OLE DB...");
145
146 //initialize connection to datasource
147 hr = pidbInitialize->Initialize();
148 SqlExitOnFailure(hr, "failed to initialize connection to database: %ls", wzDatabase);
149 156
150 hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession); 157 // try OLE DB but if that fails return original error failure
158 HRESULT hr2 = InitializeDatabaseConnection(CLSID_SQLOLEDB, "SQL OLE DB", rgdbpsetInit, countof(rgdbpsetInit), ppidbSession);
159 if (FAILED(hr2))
160 {
161 SqlExitTrace(hr2, "Could not initialize SQL OLE DB either, giving up.");
162 }
163 else
164 {
165 hr = S_OK;
166 }
167 }
151 168
152LExit: 169LExit:
153 for (; 0 < cProperties; cProperties--) 170 for (; 0 < cProperties; cProperties--)
@@ -155,8 +172,6 @@ LExit:
155 ::VariantClear(&rgdbpInit[cProperties - 1].vValue); 172 ::VariantClear(&rgdbpInit[cProperties - 1].vValue);
156 } 173 }
157 174
158 ReleaseObject(pidbProperties);
159 ReleaseObject(pidbInitialize);
160 ReleaseStr(pwzServerInstance); 175 ReleaseStr(pwzServerInstance);
161 176
162 return hr; 177 return hr;
@@ -787,6 +802,110 @@ LExit:
787// private 802// private
788// 803//
789 804
805static HRESULT InitializeDatabaseConnection(
806 __in REFCLSID rclsid,
807 __in_z LPCSTR szFriendlyClsidName,
808 __in DBPROPSET rgdbpsetInit[],
809 __in_ecount(rgdbpsetInit) DWORD cdbpsetInit,
810 __out IDBCreateSession** ppidbSession
811)
812{
813 Unused(szFriendlyClsidName); // only used in DEBUG builds
814
815 HRESULT hr = S_OK;
816 IDBInitialize* pidbInitialize = NULL;
817 IDBProperties* pidbProperties = NULL;
818
819 hr = ::CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (LPVOID*)&pidbInitialize);
820 SqlExitOnFailure(hr, "failed to initialize %s", szFriendlyClsidName);
821
822 // create and set the property set
823 hr = pidbInitialize->QueryInterface(IID_IDBProperties, (LPVOID*)&pidbProperties);
824 SqlExitOnFailure(hr, "failed to get IID_IDBProperties for %s", szFriendlyClsidName);
825
826 hr = pidbProperties->SetProperties(cdbpsetInit, rgdbpsetInit);
827 SqlExitOnFailure(hr, "failed to set properties for %s", szFriendlyClsidName);
828
829 // initialize connection to datasource
830 hr = pidbInitialize->Initialize();
831 if (FAILED(hr))
832 {
833 DumpErrorRecords();
834 }
835 SqlExitOnFailure(hr, "failed to initialize connection for %s", szFriendlyClsidName);
836
837 hr = pidbInitialize->QueryInterface(IID_IDBCreateSession, (LPVOID*)ppidbSession);
838 SqlExitOnFailure(hr, "failed to query for connection session for %s", szFriendlyClsidName);
839
840LExit:
841 ReleaseObject(pidbProperties);
842 ReleaseObject(pidbInitialize);
843
844 return hr;
845}
846
847HRESULT DumpErrorRecords()
848{
849 HRESULT hr = S_OK;
850 IErrorInfo* pIErrorInfo = NULL;
851 IErrorRecords* pIErrorRecords = NULL;
852 IErrorInfo* pIErrorInfoRecord = NULL;
853 BSTR bstrDescription = NULL;
854 ULONG i = 0;
855 ULONG cRecords = 0;
856 ERRORINFO ErrorInfo = { };
857
858 // Get IErrorInfo pointer from OLE.
859 hr = ::GetErrorInfo(0, &pIErrorInfo);
860 if (FAILED(hr))
861 {
862 ExitFunction();
863 }
864
865 // QI for IID_IErrorRecords.
866 hr = pIErrorInfo->QueryInterface(IID_IErrorRecords, (void**)&pIErrorRecords);
867 if (FAILED(hr))
868 {
869 ExitFunction();
870 }
871
872 // Get error record count.
873 hr = pIErrorRecords->GetRecordCount(&cRecords);
874 if (FAILED(hr))
875 {
876 ExitFunction();
877 }
878
879 // Loop through the error records.
880 for (i = 0; i < cRecords; i++)
881 {
882 // Get pIErrorInfo from pIErrorRecords.
883 hr = pIErrorRecords->GetErrorInfo(i, 1033, &pIErrorInfoRecord);
884
885 if (SUCCEEDED(hr))
886 {
887 // Get error description and source.
888 hr = pIErrorInfoRecord->GetDescription(&bstrDescription);
889
890 // Retrieve the ErrorInfo structures.
891 hr = pIErrorRecords->GetBasicErrorInfo(i, &ErrorInfo);
892
893 SqlExitTrace(ErrorInfo.hrError, "SQL error %lu/%lu: %ls", i + 1, cRecords, bstrDescription);
894
895 ReleaseNullObject(pIErrorInfoRecord);
896 ReleaseNullBSTR(bstrDescription);
897 }
898 }
899
900LExit:
901 ReleaseNullBSTR(bstrDescription);
902 ReleaseObject(pIErrorInfoRecord);
903 ReleaseObject(pIErrorRecords);
904 ReleaseObject(pIErrorInfo);
905
906 return hr;
907}
908
790/******************************************************************** 909/********************************************************************
791 FileSpecToString 910 FileSpecToString
792 911