Leaked source code of windows server 2003
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.

788 lines
21 KiB

  1. // Interfaces.cpp : Implementation of TSUserExInterfaces class.
  2. #include "stdafx.h"
  3. #if 1 //POST_BETA_3
  4. #include <sspi.h>
  5. #include <secext.h>
  6. #include <dsgetdc.h>
  7. #endif //POST_BETA_3
  8. //#include "ConfigDlg.h" // for CTSUserProperties
  9. #include "tsusrsht.h"
  10. //#include "logmsg.h"
  11. #include "limits.h" // USHRT_MAX
  12. #ifdef _RTM_
  13. #include <ntverp.h> // VER_PRODUCTVERSION_DW
  14. #endif
  15. #include <winsta.h>
  16. //#include "ntdsapi.h" // enbable for "having some fun macro"
  17. // clipboard format to retreive the machine name and account name associated
  18. // with a data object created by local user manager
  19. #define CCF_LOCAL_USER_MANAGER_MACHINE_NAME TEXT("Local User Manager Machine Focus Name")
  20. #define ByteOffset(base, offset) (((LPBYTE)base)+offset)
  21. BOOL g_bPagesHaveBeenInvoked = FALSE;
  22. /////////////////////////////////////////////////////////////////////////////
  23. // IExtendPropertySheet implementation
  24. HRESULT GetMachineAndUserName(IDataObject *pDataObject, LPWSTR pMachineName, LPWSTR pUserName , PBOOL pbDSAType , PSID *ppUserSid )
  25. {
  26. ASSERT_(pUserName);
  27. ASSERT_(pMachineName);
  28. ASSERT_(pDataObject != NULL );
  29. // register the display formats.
  30. // first 2 formats supported by local user manager snapin
  31. static UINT s_cfMachineName = RegisterClipboardFormat(CCF_LOCAL_USER_MANAGER_MACHINE_NAME);
  32. static UINT s_cfDisplayName = RegisterClipboardFormat(CCF_DISPLAY_NAME);;
  33. static UINT s_cfDsObjectNames = RegisterClipboardFormat(CFSTR_DSOBJECTNAMES); // this format is supported by dsadmin snapin.
  34. ASSERT_(s_cfMachineName);
  35. ASSERT_(s_cfDisplayName);
  36. ASSERT_(s_cfDsObjectNames);
  37. FORMATETC fmte = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  38. STGMEDIUM medium = { TYMED_HGLOBAL, NULL, NULL };
  39. HRESULT hr = S_OK;
  40. ASSERT_(USHRT_MAX > s_cfDsObjectNames);
  41. // first we will try dsdataobject format. This means we are running in context of dsadmin
  42. fmte.cfFormat = ( USHORT )s_cfDsObjectNames;
  43. hr = pDataObject->GetData(&fmte, &medium);
  44. if( SUCCEEDED( hr ) )
  45. {
  46. // CFSTR_DSOBJECTNAMES is supported.
  47. // It means we are dealing with dsadmin
  48. // lets get username, and domain name from the dsadmin.
  49. LPDSOBJECTNAMES pDsObjectNames = (LPDSOBJECTNAMES)medium.hGlobal;
  50. *pbDSAType = TRUE;
  51. if( pDsObjectNames->cItems < 1 )
  52. {
  53. ODS( L"TSUSEREX : @GetMachineAndUserName DS Object names < 1\n" );
  54. return E_FAIL;
  55. }
  56. LPWSTR pwszObjName = ( LPWSTR )ByteOffset( pDsObjectNames , pDsObjectNames->aObjects[0].offsetName );
  57. KdPrint( ( "TSUSEREX : adspath is %ws\n" , pwszObjName ) );
  58. // first stage get the server name from the adspath
  59. // since IADsPathname does not live off a normal IADs Directory object
  60. // so me must cocreate the object set the path and then retrieve the server name
  61. // hey this saves us wire-tripping
  62. // IADsPathname *pPathname = NULL;
  63. IADsObjectOptions *pADsOptions = NULL;
  64. IADs *pADs = NULL;
  65. hr = ADsGetObject( pwszObjName, IID_IADs, (void**)&pADs );
  66. if( FAILED( hr ) )
  67. {
  68. KdPrint( ( "TSUSEREX : no means of binding to adspath -- hresult = 0x%x\n" , hr ) );
  69. return hr;
  70. }
  71. VARIANT varServerName;
  72. VariantInit(&varServerName);
  73. hr = pADs->QueryInterface( IID_IADsObjectOptions , ( void ** )&pADsOptions );
  74. KdPrint( ( "TSUSEREX : binded to adsobject queried for IID_IADsObjectOptions returned 0x%x\n" , hr ) );
  75. if( SUCCEEDED( hr ) )
  76. {
  77. hr = pADsOptions->GetOption( ADS_OPTION_SERVERNAME, &varServerName);
  78. pADsOptions->Release( );
  79. KdPrint( ( "TSUSEREX: GetOption returned 0x%x\n" , hr ) ) ;
  80. }
  81. if( SUCCEEDED( hr ) )
  82. {
  83. lstrcpy( pMachineName , V_BSTR( &varServerName ) );
  84. KdPrint( ( "TSUSEREX: Server name is %ws\n" , pMachineName ) ) ;
  85. }
  86. VariantClear( &varServerName );
  87. if( FAILED( hr ) )
  88. {
  89. // ADS_FORMAT_SERVER is not supported this could mean we're dealing with an WinNT format
  90. // or a DS Provider that is poorly implemented
  91. KdPrint( ( "IADsPathname could not obtain server name 0x%x\n" , hr ) );
  92. // let's go wire tapping to get the server name
  93. VARIANT v;
  94. LPTSTR szDName = NULL;
  95. ULONG ulDName = 0;
  96. VariantInit(&v);
  97. hr = pADs->Get(L"distinguishedName", &v);
  98. if( FAILED( hr ) )
  99. {
  100. KdPrint( ( "TSUSEREX : pADs->Get( DN ) returned 0x%x\n", hr ) );
  101. pADs->Release();
  102. return hr;
  103. }
  104. ASSERT_( V_VT( &v ) == VT_BSTR );
  105. if( !TranslateNameW( V_BSTR(&v), NameFullyQualifiedDN, NameCanonical, szDName, &ulDName) )
  106. {
  107. KdPrint( ( "TSUSEREX : TranslateNameW failed with 0x%x\n", GetLastError( ) ) );
  108. pADs->Release();
  109. return E_FAIL;
  110. }
  111. szDName = ( LPTSTR )new TCHAR[ ulDName + 1 ];
  112. if( szDName == NULL )
  113. {
  114. KdPrint( ( "TSUSEREX : could not allocate space for szDName\n" ) );
  115. pADs->Release();
  116. return E_OUTOFMEMORY;
  117. }
  118. if( !TranslateNameW( V_BSTR(&v), NameFullyQualifiedDN, NameCanonical, szDName, &ulDName) )
  119. {
  120. KdPrint( ( "TSUSEREX : TranslateNameW failed 2nd pass with 0x%x\n", GetLastError( ) ) );
  121. delete[] szDName;
  122. pADs->Release();
  123. return E_FAIL;
  124. }
  125. // perform LEFT$( szDName , up to '/' )
  126. KdPrint( ( "TSUSEREX : TranslateNameW cracked the name to %ws\n" , szDName ) );
  127. LPTSTR pszTemp = szDName;
  128. while( pszTemp != NULL )
  129. {
  130. if( *pszTemp == L'/' )
  131. {
  132. *pszTemp = 0;
  133. break;
  134. }
  135. pszTemp++;
  136. }
  137. KdPrint( ("TranslateName with my LEFT$ returned %ws\n",szDName ) );
  138. // get the domaincontroller name of the remote machine
  139. DOMAIN_CONTROLLER_INFO *pdinfo;
  140. DWORD dwStatus = DsGetDcName( NULL , szDName , NULL , NULL , 0 , &pdinfo );
  141. KdPrint( ( "TSUSEREX : DsGetDcName: %ws returned 0x%x\n", pdinfo->DomainControllerName , dwStatus ) );
  142. if( dwStatus == NO_ERROR )
  143. {
  144. lstrcpy( pMachineName , pdinfo->DomainControllerName );
  145. NetApiBufferFree( pdinfo );
  146. }
  147. if( szDName != NULL )
  148. {
  149. delete[] szDName;
  150. }
  151. VariantClear( &v );
  152. } // END else
  153. pADs->Release( );
  154. IADsUser *pADsUser = NULL;
  155. hr = ADsGetObject( pwszObjName, IID_IADsUser, (void**)&pADsUser);
  156. if( FAILED( hr ) )
  157. {
  158. KdPrint( ( "TSUSEREX: ADsGetObject failed to get the user object 0x%x\n",hr ) );
  159. return hr;
  160. }
  161. VARIANT var;
  162. VARIANT varSid;
  163. VariantInit(&var);
  164. VariantInit(&varSid);
  165. hr = pADsUser->Get(L"ObjectSid", &varSid);
  166. if( FAILED( hr ) )
  167. {
  168. ODS( L"TSUSEREX : IADsUser::Get( ObjectSid ) failed \n" );
  169. pADsUser->Release();
  170. return hr;
  171. }
  172. if( !( varSid.vt & VT_ARRAY) )
  173. {
  174. ODS( L"TSUSEREX : Object SID is not a VT_ARRAY\n" );
  175. pADsUser->Release();
  176. return E_FAIL;
  177. }
  178. PSID pSid = NULL;
  179. PSID pUserSid = NULL;
  180. SafeArrayAccessData( varSid.parray, &pSid );
  181. if( !IsValidSid( pSid ) )
  182. {
  183. ODS( L"TSUSEREX : pSid is invalid\n" );
  184. pADsUser->Release();
  185. return E_FAIL;
  186. }
  187. DWORD dwSidSize = GetLengthSid( pSid );
  188. pUserSid = new BYTE[ dwSidSize ];
  189. if( pUserSid == NULL )
  190. {
  191. ODS( L"TSUSEREX : failed to allocate pUserSid\n" );
  192. pADsUser->Release();
  193. return E_FAIL;
  194. }
  195. CopySid( dwSidSize , pUserSid , pSid );
  196. *ppUserSid = pUserSid;
  197. SafeArrayUnaccessData( varSid.parray );
  198. VariantClear( &varSid );
  199. hr = pADsUser->Get( L"samAccountName" , &var );
  200. pADsUser->Release();
  201. if( FAILED( hr ) )
  202. {
  203. KdPrint( ( "TSUSEREX : ADsUser::Get( name ) failed 0x%x\n", hr ) );
  204. return hr;
  205. }
  206. ASSERT_( V_VT( &var ) == VT_BSTR );
  207. lstrcpy( pUserName , V_BSTR( &var ) );
  208. KdPrint( ( "TSUSEREX : Server name %ws user name is %ws\n" , pMachineName , pUserName ) );
  209. VariantClear( &var );
  210. ReleaseStgMedium(&medium);
  211. }
  212. else
  213. {
  214. // CFSTR_DSOBJECTNAMES is NOT supported.
  215. // It means we are dealing with local user manager.
  216. // we must be able to get
  217. // Allocate medium for GetDataHere.
  218. medium.hGlobal = GlobalAlloc(GMEM_SHARE, MAX_PATH * sizeof(WCHAR));
  219. if( !medium.hGlobal )
  220. {
  221. ODS( L"TSUSEREX : GlobalAlloc failed in GetMachineAndUserName\n" );
  222. return E_OUTOFMEMORY;
  223. }
  224. *pbDSAType = FALSE;
  225. // since we are doing data conversion.
  226. // check for possible data loss.
  227. ASSERT_(USHRT_MAX > s_cfMachineName);
  228. // request the machine name from the dataobject.
  229. fmte.cfFormat = (USHORT)s_cfMachineName;
  230. hr = pDataObject->GetDataHere(&fmte, &medium);
  231. if( FAILED( hr ) )
  232. {
  233. ODS( L"TSUSEREX : @GetMachineAndUserName GetDataHere for s_cfMachineName failed\n" );
  234. return hr;
  235. }
  236. // copy the machine name into our buffer
  237. if( ( LPWSTR )medium.hGlobal != NULL && pMachineName != NULL )
  238. {
  239. wcscpy(pMachineName, (LPWSTR)medium.hGlobal );
  240. }
  241. // administer local accounts only for Terminal Servers
  242. SERVER_INFO_101 *psi101;
  243. HANDLE hTServer = NULL;
  244. DWORD dwNetStatus = NetServerGetInfo( pMachineName , 101 , ( LPBYTE * )&psi101 );
  245. if( dwNetStatus != NERR_Success )
  246. {
  247. KdPrint( ( "TSUSEREX:GetMachineAndUserName -- NetServerGetInfo failed with 0x%x\n", dwNetStatus ) );
  248. return E_FAIL;
  249. }
  250. if( psi101 == NULL )
  251. {
  252. KdPrint( ( "TSUSEREX:GetMachineAndUserName -- NetServerGetInfo failed getting sinfo101 0x%x\n",dwNetStatus ) );
  253. return E_FAIL;
  254. }
  255. KdPrint( ("TSUSEREX:NetServerGetInfo server bits returnd 0x%x and nttype is 0x%x\n", psi101->sv101_type , SV_TYPE_SERVER_NT ) );
  256. BOOL fServer = ( BOOL )( psi101->sv101_type & ( DWORD )SV_TYPE_SERVER_NT );
  257. NetApiBufferFree( ( LPVOID )psi101 );
  258. if( !fServer )
  259. {
  260. KdPrint( ( "TSUSEREX : viewing local account on non-TS ( exiting )\n" ) );
  261. return E_FAIL;
  262. }
  263. hTServer = WinStationOpenServer( pMachineName );
  264. if( hTServer == NULL )
  265. {
  266. KdPrint( ( "TSUSEREX: This OS does not support terminal services\n" ) ) ;
  267. return E_FAIL;
  268. }
  269. WinStationCloseServer( hTServer );
  270. // since we are doing data conversion.
  271. // check for possible data loss.
  272. ASSERT_(USHRT_MAX > s_cfDisplayName);
  273. // request data about user name.
  274. fmte.cfFormat = (USHORT)s_cfDisplayName;
  275. hr = pDataObject->GetDataHere( &fmte , &medium );
  276. if( FAILED( hr ) )
  277. {
  278. ODS( L"TSUSEREX : @GetMachineAndUserName GetDataHere for s_cfDisplayName failed\n" );
  279. return hr;
  280. }
  281. // copy the user name into our buffer and release the medium.
  282. if( ( LPWSTR )medium.hGlobal != NULL && pUserName != NULL )
  283. {
  284. wcscpy( pUserName , ( LPWSTR )medium.hGlobal );
  285. }
  286. ReleaseStgMedium( &medium );
  287. }
  288. return S_OK;
  289. }
  290. //-----------------------------------------------------------------------------------------------------
  291. TSUserExInterfaces::TSUserExInterfaces()
  292. {
  293. // LOGMESSAGE0(_T("TSUserExInterfaces::TSUserExInterfaces()..."));
  294. // m_pUserConfigPage = NULL;
  295. m_pTSUserSheet = NULL;
  296. m_pDsadataobj = NULL;
  297. }
  298. //-----------------------------------------------------------------------------------------------------
  299. TSUserExInterfaces::~TSUserExInterfaces()
  300. {
  301. ODS( L"Good bye\n" );
  302. }
  303. //-----------------------------------------------------------------------------------------------------
  304. HRESULT TSUserExInterfaces::CreatePropertyPages(
  305. LPPROPERTYSHEETCALLBACK lpProvider, // pointer to the callback interface
  306. LONG_PTR , // handle for routing notification
  307. LPDATAOBJECT lpIDataObject) // pointer to the data object);
  308. {
  309. //
  310. // Test for valid parameters
  311. //
  312. if( lpIDataObject == NULL || IsBadReadPtr( lpIDataObject , sizeof( LPDATAOBJECT ) ) )
  313. {
  314. ODS( L"TSUSEREX : @ CreatePropertyPages IDataObject is invalid\n " );
  315. return E_INVALIDARG;
  316. }
  317. if( lpProvider == NULL )
  318. {
  319. ODS( L"TSUSEREX @ CreatePropertyPages LPPROPERTYSHEETCALLBACK is invalid\n" );
  320. return E_INVALIDARG;
  321. }
  322. WCHAR wUserName[ MAX_PATH ];
  323. WCHAR wMachineName[ MAX_PATH ];
  324. BOOL bDSAType;
  325. if( g_bPagesHaveBeenInvoked )
  326. {
  327. ODS( L"TSUSEREX : TSUserExInterfaces::CreatePropertyPages pages have been invoked\n" );
  328. return E_FAIL;
  329. }
  330. PSID pUserSid = NULL;
  331. if( FAILED( GetMachineAndUserName( lpIDataObject , wMachineName , wUserName , &bDSAType , &pUserSid ) ) )
  332. {
  333. ODS( L"TSUSEREX : GetMachineAndUserName failed @CreatePropertyPages \n" );
  334. return E_FAIL;
  335. }
  336. //
  337. // Test to see if we are being called twice
  338. //
  339. if( m_pTSUserSheet != NULL )
  340. {
  341. return E_FAIL;
  342. }
  343. //
  344. // MMC likes to release IEXtendPropertySheet ( this object )
  345. // so we cannot free CTSUserSheet in TSUserExInterfaces::dtor
  346. // CTSUserSheet must release itself!!!
  347. //
  348. m_pTSUserSheet = new CTSUserSheet( );
  349. if( m_pTSUserSheet != NULL )
  350. {
  351. ODS( L"TSUSEREX : CreatePropertyPages mem allocation succeeded\n" );
  352. m_pTSUserSheet->SetDSAType( bDSAType );
  353. VERIFY_S( TRUE , m_pTSUserSheet->SetServerAndUser( &wMachineName[0] , &wUserName[0] ) );
  354. m_pTSUserSheet->CopyUserSid( pUserSid );
  355. VERIFY_S( S_OK , m_pTSUserSheet->AddPagesToPropSheet( lpProvider ) );
  356. }
  357. return S_OK;
  358. }
  359. //-----------------------------------------------------------------------------------------------------
  360. HRESULT TSUserExInterfaces::QueryPagesFor( LPDATAOBJECT /* lpDataObject */ )
  361. {
  362. return S_OK;
  363. }
  364. //-----------------------------------------------------------------------------------------------------
  365. // this has not been checked in yet!!!
  366. //-----------------------------------------------------------------------------------------------------
  367. STDMETHODIMP TSUserExInterfaces::GetHelpTopic( LPOLESTR *ppszHelp )
  368. {
  369. ODS( L"TSUSEREX : GetHelpTopic\n" );
  370. if( ppszHelp == NULL )
  371. {
  372. return E_INVALIDARG;
  373. }
  374. TCHAR tchHelpFile[ 80 ];
  375. VERIFY_E( 0 , LoadString( _Module.GetResourceInstance( ) , IDS_TSUSERHELP , tchHelpFile , sizeof( tchHelpFile ) / sizeof( TCHAR ) ) );
  376. // mmc will call CoTaskMemFree
  377. *ppszHelp = ( LPOLESTR )CoTaskMemAlloc( sizeof( TCHAR ) * MAX_PATH );
  378. if( *ppszHelp != NULL )
  379. {
  380. if( GetSystemWindowsDirectory( *ppszHelp , MAX_PATH ) != 0 )
  381. {
  382. lstrcat( *ppszHelp , tchHelpFile );
  383. }
  384. else
  385. {
  386. lstrcpy( *ppszHelp , tchHelpFile );
  387. }
  388. ODS( *ppszHelp );
  389. ODS( L"\n" );
  390. return S_OK;
  391. }
  392. return E_OUTOFMEMORY;
  393. }
  394. //-----------------------------------------------------------------------------------------------------
  395. // IShellExtInit
  396. STDMETHODIMP TSUserExInterfaces::Initialize(
  397. LPCITEMIDLIST ,
  398. LPDATAOBJECT lpdobj,
  399. HKEY
  400. )
  401. {
  402. m_pDsadataobj = lpdobj;
  403. return S_OK;
  404. }
  405. //-----------------------------------------------------------------------------------------------------
  406. // IShellPropSheetExt - this interface is used only for dsadmin based tools
  407. // for this reason the DSAType flag is set to true.
  408. STDMETHODIMP TSUserExInterfaces::AddPages(
  409. LPFNADDPROPSHEETPAGE lpfnAddPage,
  410. LPARAM lParam
  411. )
  412. {
  413. //
  414. // Test for valid parameters
  415. //
  416. if( m_pDsadataobj == NULL )
  417. {
  418. ODS( L"TSUSEREX : @ AddPages IDataObject is invalid\n " );
  419. return E_INVALIDARG;
  420. }
  421. if( lpfnAddPage == NULL )
  422. {
  423. ODS( L"TSUSEREX @ AddPages LPFNADDPROPSHEETPAGE is invalid\n" );
  424. return E_INVALIDARG;
  425. }
  426. WCHAR wUserName[ MAX_PATH ];
  427. WCHAR wMachineName[ MAX_PATH ];
  428. BOOL bDSAType;
  429. PSID pUserSid = NULL;
  430. if( FAILED( GetMachineAndUserName( m_pDsadataobj , wMachineName , wUserName , &bDSAType , &pUserSid ) ) )
  431. {
  432. ODS( L"TSUSEREX : GetMachineAndUserName @AddPages failed \n" );
  433. return E_FAIL;
  434. }
  435. ODS( L"TSUSEREX : DSATYPE in AddPages\n" );
  436. g_bPagesHaveBeenInvoked = TRUE;
  437. //
  438. // Test to see if we are being called twice
  439. //
  440. if( m_pTSUserSheet != NULL )
  441. {
  442. return E_FAIL;
  443. }
  444. //
  445. // MMC likes to release IEXtendPropertySheet ( this object )
  446. // so we cannot free CTSUserSheet in TSUserExInterfaces::dtor
  447. // CTSUserSheet must release itself!!!
  448. //
  449. m_pTSUserSheet = new CTSUserSheet( );
  450. if( m_pTSUserSheet != NULL )
  451. {
  452. ODS( L"TSUSEREX : AddPages mem allocation succeeded\n" );
  453. m_pTSUserSheet->SetDSAType( bDSAType );
  454. m_pTSUserSheet->CopyUserSid( pUserSid );
  455. VERIFY_S( TRUE , m_pTSUserSheet->SetServerAndUser( &wMachineName[0] , &wUserName[0] ) );
  456. VERIFY_S( S_OK , m_pTSUserSheet->AddPagesToDSAPropSheet( lpfnAddPage , lParam ) );
  457. }
  458. return S_OK;
  459. }
  460. //-----------------------------------------------------------------------------------------------------
  461. STDMETHODIMP TSUserExInterfaces::ReplacePage(
  462. UINT ,
  463. LPFNADDPROPSHEETPAGE ,
  464. LPARAM
  465. )
  466. {
  467. return E_FAIL;
  468. }
  469. #ifdef _RTM_ // add ISnapinAbout
  470. //-----------------------------------------------------------------------------------------------------
  471. STDMETHODIMP TSUserExInterfaces::GetSnapinDescription(
  472. LPOLESTR *ppOlestr )
  473. {
  474. TCHAR tchMessage[] = TEXT("This extension allows the administrator to configure Terminal Services user properties. This extension is only enabled on Terminal Servers.");
  475. ODS( L"TSUSEREX: GetSnapinDescription called\n" );
  476. *ppOlestr = ( LPOLESTR )CoTaskMemAlloc( ( lstrlen( tchMessage ) + 1 ) * sizeof( TCHAR ) );
  477. if( *ppOlestr != NULL )
  478. {
  479. lstrcpy( *ppOlestr , tchMessage );
  480. return S_OK;
  481. }
  482. return E_OUTOFMEMORY;
  483. }
  484. //-----------------------------------------------------------------------------------------------------
  485. STDMETHODIMP TSUserExInterfaces::GetProvider(
  486. LPOLESTR *ppOlestr )
  487. {
  488. TCHAR tchMessage[] = TEXT("Microsoft Corporation");
  489. ODS( L"TSUSEREX: GetProvider called\n" );
  490. *ppOlestr = ( LPOLESTR )CoTaskMemAlloc( ( lstrlen( tchMessage ) + 1 ) * sizeof( TCHAR ) );
  491. if( *ppOlestr != NULL )
  492. {
  493. lstrcpy( *ppOlestr , tchMessage );
  494. return S_OK;
  495. }
  496. return E_OUTOFMEMORY;
  497. }
  498. //-----------------------------------------------------------------------------------------------------
  499. STDMETHODIMP TSUserExInterfaces::GetSnapinVersion(
  500. LPOLESTR *ppOlestr )
  501. {
  502. char chMessage[ 32 ] = VER_PRODUCTVERSION_STR;
  503. TCHAR tchMessage[32];
  504. ODS( L"TSUSEREX: GetSnapinVersion called\n" );
  505. int iCharCount = MultiByteToWideChar( CP_ACP , 0 , chMessage , sizeof( chMessage ) , tchMessage , sizeof( tchMessage ) / sizeof( TCHAR ) );
  506. //wsprintf( tchMessage , TEXT( "%d" ) , VER_PRODUCTVERSION_DW );
  507. *ppOlestr = ( LPOLESTR )CoTaskMemAlloc( ( iCharCount + 1 ) * sizeof( TCHAR ) );
  508. if( *ppOlestr != NULL )
  509. {
  510. lstrcpy( *ppOlestr , tchMessage );
  511. return S_OK;
  512. }
  513. return E_OUTOFMEMORY;
  514. }
  515. //-----------------------------------------------------------------------------------------------------
  516. STDMETHODIMP TSUserExInterfaces::GetSnapinImage(
  517. HICON * )
  518. {
  519. return E_NOTIMPL;
  520. }
  521. //-----------------------------------------------------------------------------------------------------
  522. STDMETHODIMP TSUserExInterfaces::GetStaticFolderImage(
  523. /* [out] */ HBITMAP *,
  524. /* [out] */ HBITMAP *,
  525. /* [out] */ HBITMAP *,
  526. /* [out] */ COLORREF *)
  527. {
  528. return E_NOTIMPL;
  529. }
  530. #endif //_RTM_