summaryrefslogtreecommitdiffstats
path: root/private/oleutest/cfmex
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/oleutest/cfmex
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/oleutest/cfmex')
-rw-r--r--private/oleutest/cfmex/cdir.cxx418
-rw-r--r--private/oleutest/cfmex/cdir.hxx161
-rw-r--r--private/oleutest/cfmex/cfmex.cxx449
-rw-r--r--private/oleutest/cfmex/cfmex.hxx31
-rw-r--r--private/oleutest/cfmex/cmoniker.cxx1684
-rw-r--r--private/oleutest/cfmex/cmoniker.hxx178
-rw-r--r--private/oleutest/cfmex/ctest.cxx1283
-rw-r--r--private/oleutest/cfmex/ctest.hxx118
-rw-r--r--private/oleutest/cfmex/makefile6
-rw-r--r--private/oleutest/cfmex/sources35
10 files changed, 4363 insertions, 0 deletions
diff --git a/private/oleutest/cfmex/cdir.cxx b/private/oleutest/cfmex/cdir.cxx
new file mode 100644
index 000000000..250404676
--- /dev/null
+++ b/private/oleutest/cfmex/cdir.cxx
@@ -0,0 +1,418 @@
+
+//+==========================================================================
+//
+// File: CDir.cxx
+//
+// Purpose: Define the CDirectory class.
+//
+// This class is used to represent a directory name.
+// Along with maintaining the name, it can determine
+// the type of FileSystem.
+//
+//+==========================================================================
+
+
+// --------
+// Includes
+// --------
+
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <wtypes.h>
+
+#include "CFMEx.hxx"
+#include "CDir.hxx"
+
+
+//+------------------------------------------------------------------------------
+//
+// Function: CDirectory::Initialize (no arguments)
+//
+// Synopsis: Generate a directory name, using the TEMP environment
+// variable, and use it to initialize this object.
+//
+// Inputs: None.
+//
+// Outputs: TRUE if the function succeeds, FALSE otherwise.
+//
+//+------------------------------------------------------------------------------
+
+BOOL CDirectory::Initialize()
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ // Assume failure for now.
+ BOOL bSuccess = FALSE;
+
+ // The TEMP environment variable.
+ WCHAR wszSystemTempPath[ MAX_PATH + sizeof( L'\0' )];
+
+ // Reset the error code.
+ m_lError = 0L;
+
+ // ----------
+ // Get %TEMP%
+ // ----------
+
+ if( !GetTempPath( MAX_PATH,
+ wszSystemTempPath )
+ )
+ {
+ m_lError = GetLastError();
+ EXIT( L"GetTempPath() failed (%d)" );
+ }
+
+ // ----------------------
+ // Initialize this object
+ // ----------------------
+
+ // Initialize using the temporary path. We must never pass a NULL here,
+ // or we'll cause an infinite recursion.
+
+ if( wszSystemTempPath == NULL )
+ EXIT( L"Invalid temporary path" );
+ bSuccess = Initialize( wszSystemTempPath );
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ return( bSuccess );
+
+}
+
+//+-----------------------------------------------------------------------
+//
+// Function: CDirectory::Initialize (with an ANSI string)
+//
+// Synopsis: This function converts the ANSI string to a Unicode
+// string, then initializes the object with it.
+//
+// Inputs: A Unicode string.
+//
+// Outputs: TRUE if the function succeeds, FALSE otherwise.
+//
+//+-----------------------------------------------------------------------
+
+BOOL CDirectory::Initialize( LPCSTR szDirectory )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ // Assume failure.
+ BOOL bSuccess = FALSE;
+
+ // A buffer for the Unicode path
+ WCHAR wszDirectory[ MAX_UNICODE_PATH + sizeof( L'\0' )];
+
+ // -----
+ // Begin
+ // -----
+
+ // Initialize the error code.
+ m_lError = 0L;
+
+
+ // If we were givin a NULL path, use the version of Initialize
+ // that requires no path.
+
+ if( szDirectory == NULL )
+ {
+ bSuccess = Initialize();
+ goto Exit;
+ }
+
+ // Convert the Ansi name to Unicode.
+
+ if( m_lError = (long) AnsiToUnicode( szDirectory,
+ wszDirectory,
+ strlen( szDirectory )
+ )
+ )
+ {
+ EXIT( L"Unable to convert directory to Unicode" );
+ }
+
+ // Initialize using the temporary path. We must never pass a NULL here,
+ // or we'll cause an infinite recursion.
+
+ if( wszDirectory == NULL )
+ EXIT( L"Invalid Directory (internal error)" );
+ bSuccess = Initialize( wszDirectory );
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ return( bSuccess );
+
+}
+
+
+//+-----------------------------------------------------------------------
+//
+// Function: CDirectory::Initialize (with a Unicode string)
+//
+// Synopsis: This function is the only form of Initialize
+// (there are several variations of the Initialize member)
+// which really initializes the object. It stores the
+// directory name, and determines the type of filesystem
+// on which it resides.
+//
+// Inputs: A Unicode string.
+//
+// Outputs: TRUE if the function succeeds, FALSE otherwise.
+//
+//+-----------------------------------------------------------------------
+
+BOOL CDirectory::Initialize( LPCWSTR wszDirectory )
+{
+
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ // Assume failure.
+ BOOL bSuccess = FALSE;
+
+ // Buffers for the root of the path and for the volume name.
+ WCHAR wszDirectoryRoot[ MAX_PATH + sizeof( L'\0' )];
+ WCHAR wszVolumeName[ MAX_PATH + sizeof( L'\0' )];
+
+ // Parameters to GetVolumeInformation which we won't use.
+ DWORD dwMaxComponentLength = 0L;
+ DWORD dwFileSystemFlags = 0L;
+
+ // -----
+ // Begin
+ // -----
+
+ // Initialize the error code.
+ m_lError = 0L;
+
+ // If we were given a NULL path, use the variation of Initialization()
+ // which does not require one. Note that we will then be called again,
+ // but this time with a path.
+
+ if( wszDirectory == NULL )
+ {
+ bSuccess = Initialize();
+ goto Exit;
+ }
+
+ // Validate the path.
+
+ if( wcslen( wszDirectory ) > MAX_PATH )
+ {
+ m_lError = wcslen( wszDirectory );
+ EXIT( L"Input path is too long (%d)\n" );
+ }
+
+ // Save the path to our member buffer
+
+ wcscpy( m_wszDirectory, wszDirectory );
+
+
+ // ------------------------
+ // Get the file system name
+ // ------------------------
+
+ // Get the root path to the directory.
+
+ wcscpy( wszDirectoryRoot, wszDirectory );
+ MakeRoot( wszDirectoryRoot );
+
+ // Get the volume information, which will include the filesystem name.
+
+ if( !GetVolumeInformation( wszDirectoryRoot, // Root path name.
+ wszVolumeName, // Buffer for volume name
+ MAX_PATH, // Length of the above buffer
+ NULL, // Buffer for serial number
+ // Longest filename length.
+ &dwMaxComponentLength,
+ &dwFileSystemFlags, // Compression, etc.
+ m_wszFileSystemName,// Buffer for the FS name.
+ MAX_PATH ) // Length of above buffer
+ )
+ {
+ m_lError = GetLastError();
+ EXIT( L"GetVolumeInformation() failed" );
+ }
+
+
+ // Determine the file system type from the name.
+
+ if( !wcscmp( m_wszFileSystemName, L"FAT" ))
+ m_FileSystemType = fstFAT;
+
+ else if( !wcscmp( m_wszFileSystemName, L"NTFS" ))
+ m_FileSystemType = fstNTFS;
+
+ else if( !wcscmp( m_wszFileSystemName, L"OFS" ))
+ m_FileSystemType = fstOFS;
+
+ else
+ m_FileSystemType = fstUnknown;
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CDirectory::Initialize( wszDirectory )" );
+ return( bSuccess );
+
+}
+
+
+
+//
+// GetRootLength
+//
+// This routine was simply copied from private\windows\shell\shelldll\tracker.cxx,
+// and should not be modified here.
+//
+
+unsigned
+CDirectory::GetRootLength(const WCHAR *pwszPath)
+{
+ ULONG cwcRoot = 0;
+ m_lError = 0L;
+
+ if (pwszPath == 0)
+ pwszPath = L"";
+
+ if (*pwszPath == L'\\')
+ {
+ // If the first character is a path separator (backslash), this
+ // must be a UNC drive designator which must be of the form:
+ // <path-separator><path-separator>(<alnum>+)
+ // <path-separator>(<alnum>+)<path-separator>
+ //
+ // This covers drives like these: \\worf\scratch\ and
+ // \\savik\win4dev\.
+ //
+ pwszPath++;
+ cwcRoot++;
+
+ BOOL fMachine = FALSE;
+ BOOL fShare = FALSE;
+
+ if (*pwszPath == L'\\')
+ {
+ cwcRoot++;
+ pwszPath++;
+
+ while (*pwszPath != '\0' && *pwszPath != L'\\')
+ {
+ cwcRoot++;
+ pwszPath++;
+
+ fMachine = TRUE;
+ }
+
+ if (*pwszPath == L'\\')
+ {
+ cwcRoot++;
+ pwszPath++;
+
+ while (*pwszPath != '\0' && *pwszPath != L'\\')
+ {
+ cwcRoot++;
+ pwszPath++;
+
+ fShare = TRUE;
+ }
+
+ // If there weren't any characters in the machine or
+ // share portions of the UNC name, then the drive
+ // designator is bogus.
+ //
+ if (!fMachine || !fShare)
+ {
+ cwcRoot = 0;
+ }
+ }
+ else
+ {
+ cwcRoot = 0;
+ }
+ }
+ else
+ {
+ cwcRoot = 0;
+ }
+ }
+ else
+ if (iswalpha(*pwszPath))
+ {
+ // If the first character is an alphanumeric, we must have
+ // a drive designator of this form:
+ // (<alnum>)+<drive-separator><path-separator>
+ //
+ // This covers drives like these: a:\, c:\, etc
+ //
+
+ pwszPath++;
+ cwcRoot++;
+
+ if (*pwszPath == L':')
+ {
+ cwcRoot++;
+ pwszPath++;
+ }
+ else
+ {
+ cwcRoot = 0;
+ }
+ }
+
+ // If we have counted one or more characters in the root and these
+ // are followed by a component separator, we need to add the separator
+ // to the root length. Otherwise this is not a valid root and we need
+ // to return a length of zero.
+ //
+ if ((cwcRoot > 0) && (*pwszPath == L'\\'))
+ {
+ cwcRoot++;
+ }
+ else
+ {
+ cwcRoot = 0;
+ }
+
+ return (cwcRoot);
+}
+
+//
+// MakeRoot
+//
+// This routine was simply copied from private\windows\shell\shelldll\tracker.cxx,
+// and should not be modified here.
+//
+
+
+VOID
+CDirectory::MakeRoot(WCHAR *pwszPath)
+{
+ unsigned rootlength = GetRootLength(pwszPath);
+ m_lError = 0L;
+
+ if (rootlength)
+ {
+ pwszPath[rootlength] = L'\0';
+ }
+}
+
diff --git a/private/oleutest/cfmex/cdir.hxx b/private/oleutest/cfmex/cdir.hxx
new file mode 100644
index 000000000..7feaf8851
--- /dev/null
+++ b/private/oleutest/cfmex/cdir.hxx
@@ -0,0 +1,161 @@
+
+
+//+-----------------------------------------------------------------------
+//
+// File: CDir.hxx
+//
+// Purpose: Declare the CDirectory class. Objects of this class
+// are used to represent a directory, and provide additional
+// information about it.
+//
+//+-----------------------------------------------------------------------
+
+
+#ifndef _C_DIR_HXX_
+#define _C_DIR_HXX_
+
+// --------
+// Includes
+// --------
+
+#include "CFMEx.hxx"
+
+
+// --------
+// Typedefs
+// --------
+
+// An enumeration of the possible file system types.
+
+typedef enum
+{
+ fstFAT,
+ fstNTFS,
+ fstOFS,
+ fstUnknown
+} enumFileSystemType;
+
+
+// ----------
+// CDirectory
+// ----------
+
+class CDirectory
+{
+
+// Construction/Deconstruction
+
+public:
+
+ CDirectory();
+ ~CDirectory();
+
+// Public Member Functions
+
+public:
+
+ BOOL Initialize(); // Defaulted input
+ BOOL Initialize( LPCWSTR wszDirectory ); // Unicode input
+ BOOL Initialize( LPCSTR szDirectory ); // ANSI input
+
+ enumFileSystemType GetFileSystemType() const;
+ LPCWSTR GetFileSystemName() const;
+ LPCWSTR GetDirectoryName() const;
+
+
+// Private Member Functions
+
+private:
+
+ void DisplayErrors( BOOL bSuccess, LPCWSTR wszFunctionName );
+ VOID MakeRoot(WCHAR *pwszPath);
+ unsigned GetRootLength(const WCHAR *pwszPath);
+
+
+// Member Data
+
+private:
+
+ WCHAR m_wszDirectory[ MAX_UNICODE_PATH + sizeof( L'\0' )];
+ WCHAR m_wszFileSystemName[ MAX_UNICODE_PATH + sizeof( L'\0' )];
+ enumFileSystemType m_FileSystemType;
+
+ WCHAR m_wszErrorMessage[ 200 ];
+ long m_lError;
+
+};
+
+
+// ----------------
+// Inline Functions
+// ----------------
+
+
+// CDirectory::CDirectory
+
+inline CDirectory::CDirectory()
+{
+ wcscpy( m_wszDirectory, L"" );
+ wcscpy( m_wszFileSystemName, L"" );
+ wcscpy( m_wszErrorMessage, L"" );
+ m_lError = 0;
+ m_FileSystemType = fstUnknown;
+
+}
+
+// CDirectory::~CDirectory
+
+inline CDirectory::~CDirectory()
+{
+}
+
+// CDirectory::GetFileSystemType
+
+inline enumFileSystemType CDirectory::GetFileSystemType() const
+{
+ return m_FileSystemType;
+}
+
+// CDirectory::GetFileSystemName
+
+inline LPCWSTR CDirectory::GetFileSystemName() const
+{
+ return m_wszFileSystemName;
+}
+
+// CDirectory::GetDirectoryName
+
+inline LPCWSTR CDirectory::GetDirectoryName() const
+{
+ return m_wszDirectory;
+}
+
+
+// CDirectory::DisplayErrors
+
+inline void CDirectory::DisplayErrors( BOOL bSuccess, LPCWSTR wszFunctionName )
+{
+ if( !bSuccess )
+ {
+ wprintf( L"Error in %s (%08x)\n %s\n",
+ wszFunctionName, m_lError, m_wszErrorMessage );
+ }
+}
+
+
+// ------
+// Macros
+// ------
+
+// Early-exit macro.
+
+#undef EXIT
+#define EXIT( error ) \
+ {\
+ wcscpy( m_wszErrorMessage, ##error );\
+ goto Exit;\
+ }
+
+
+
+#endif // _C_DIR_HXX_
diff --git a/private/oleutest/cfmex/cfmex.cxx b/private/oleutest/cfmex/cfmex.cxx
new file mode 100644
index 000000000..37e8c08ed
--- /dev/null
+++ b/private/oleutest/cfmex/cfmex.cxx
@@ -0,0 +1,449 @@
+
+//+===============================================================
+//
+// File: CFMEx.cxx
+//
+// Purpose: This file provides the main() and global functions
+// for the CreateFileMonikerEx (CFMEx) DRT.
+// This DRT tests the CreateFileMonikerEx API (new to
+// Cairo), as well as related changes to BIND_OPTS
+// (use of the BIND_OPTS2 structure).
+//
+// All moniker activity is performed in the CMoniker
+// object (such as creating a bind context, creating
+// a link source file, moving it, binding it, etc.).
+//
+// The test engine is in the CTest object. When a link
+// source file is moved to test link-tracking, the
+// original and final location of the file may be
+// specified by the user (on the command-line). CTest
+// is aware of the filesystem type (FAT, NTFS, OFS)
+// of these locations, and is aware of how that will
+// effect the results.
+//
+// This file also provides global functions (not associated
+// with an object).
+//
+// Usage: cfmex [-o<Directory>] [-f<Directory>]
+//
+// -o<Directory> specifies the original directory for link sources
+// -f<Directory> specifies the final directory for link sources
+//
+// Examples: cfmex
+// cfmex -oC:\ -fC:\
+//
+//+===============================================================
+
+
+// ----
+// TODO:
+// ----
+//
+// - Replace CRT calls with Win32 calls.
+// - Validate the directory in CDirectory.
+// - Add a flag to CDirectory to indicate "indexed".
+// - Add a flag to CDirectory to indicate local vs remote.
+// - Add a test to verify that the moniker returned from Reduce is
+// not a tracking moniker.
+//
+
+
+// -------------
+// Include Files
+// -------------
+
+
+#define _DCOM_ // Allow DCOM extensions (e.g., CoInitializeEx).
+
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <wtypes.h>
+#include <oaidl.h>
+#include <dsys.h>
+#include <olecairo.h>
+#include "CFMEx.hxx"
+#include "CMoniker.hxx"
+#include "CTest.hxx"
+#include "CDir.hxx"
+
+
+// ------
+// Macros
+// ------
+
+// Early-exit macro: put the error message in a global array,
+// and jump to Exit
+
+WCHAR wszErrorMessage[ 512 ];
+
+#undef EXIT
+#define EXIT( error ) \
+ {\
+ wcscpy( wszErrorMessage, ##error );\
+ goto Exit;\
+ }
+
+
+
+// Run a Test: Display a new paragraph on the screen, run a test,
+// and update stats.
+
+#define RUN_TEST( testID ) \
+ {\
+ nTotalTests++; \
+ wprintf( L"----------------------------------------------\n" ); \
+ wprintf( L"Test %d: ", nTotalTests ); \
+ if( cTest.##testID ) \
+ wprintf( L"Passed\n" ); \
+ else \
+ nTestsFailed++; \
+ }
+
+
+//+---------------------------------------------------------------------------------------
+//
+// Function: DisplayHelp
+//
+// Synopsis: Display a help screen with usage information.
+//
+// Inputs: None.
+//
+// Outputs: None.
+//
+// Effects: None
+//
+//+---------------------------------------------------------------------------------------
+
+
+void DisplayHelp( void )
+{
+
+ wprintf( L"This DRT tests the CreateFileMonikerEx API, and related changes.\n"
+ L"Most of these tests create a link source file, create a moniker\n"
+ L"representing that file, then move the file. You can specify the\n"
+ L"original and/or final locations of the link source, or let those\n"
+ L"locations default to the \%TEMP\% directory\n"
+ L"\n"
+ L"Usage: cfmex [-o<Directory>] [-f<Directory>]\n"
+ L"\n"
+ L"Where: -o<Directory> specifies the original directory for link sources\n"
+ L" -f<Directory> specifies the final directory for link sources\n"
+ L"\n"
+ L"Note: If an original or final directory is specified on the command-\n"
+ L" line, that directory must already exist. If one of these locations\n"
+ L" is not specified, the TEMP environment variable must be defined\n"
+ L" and it must specify an extant directory.\n"
+ L"\n"
+ L"E.g.: cfmex\n"
+ L" cfmex -oC:\\\n"
+ L" cfmex -oE:\\ -fF:\\temp\n"
+ L"\n" );
+ return;
+}
+
+
+
+//+---------------------------------------------------------------------------------------
+//
+// Function: UnicodeToAnsi
+//
+// Synopsis: Convert a Unicode (wide) string to ANSI
+//
+// Inputs: The Unicode String
+// The buffer for the ANSI string
+// The size of the above buffer
+//
+// Outputs: 0 if successful
+// GetLastError() otherwise
+//
+// Effects: None
+//
+//+---------------------------------------------------------------------------------------
+
+DWORD UnicodeToAnsi( const WCHAR * wszUnicode,
+ CHAR * szAnsi,
+ int cbAnsiMax )
+{
+ int cbAnsi = 0;
+
+ // Convert WCS to the MBCS, using the ANSI code page.
+
+ cbAnsi = WideCharToMultiByte( CP_ACP,
+ WC_COMPOSITECHECK | WC_DEFAULTCHAR,
+ wszUnicode,
+ wcslen( wszUnicode ),
+ szAnsi,
+ cbAnsiMax,
+ NULL,
+ NULL );
+ if( !cbAnsi )
+ {
+ // No characters were converted - there was an error.
+ // Note that this will be returned if a null Unicode string is
+ // passed in.
+
+ return( GetLastError() );
+ }
+ else
+ {
+ // Terminate the Ansi string and return.
+
+ szAnsi[ cbAnsi ] = '\0';
+ return( 0L );
+ }
+
+} // UnicodeToAnsi()
+
+
+
+//+---------------------------------------------------------------------------------------
+//
+// Function: AnsiToUnicode
+//
+// Synopsis: Convert an ANSI string to Unicode (i.e. Wide)
+//
+// Inputs: The ANSI String
+// The buffer for the Unicode string
+// The size of the above buffer.
+//
+// Outputs: 0 if successful
+// GetLastError() otherwise
+//
+// Effects: None
+//
+//+---------------------------------------------------------------------------------------
+
+DWORD AnsiToUnicode( const CHAR * szAnsi,
+ WCHAR * wszUnicode,
+ int cbUnicodeMax )
+{
+ int cbUnicode = 0;
+
+ cbUnicode = MultiByteToWideChar( CP_ACP,
+ MB_PRECOMPOSED,
+ szAnsi,
+ strlen( szAnsi ),
+ wszUnicode,
+ cbUnicodeMax );
+
+ if( !cbUnicode )
+ {
+ // If no characters were converted, then there was an error.
+
+ return( GetLastError() );
+ }
+ else
+ {
+ // Terminate the Unicode string and return.
+
+ wszUnicode[ cbUnicode ] = L'\0';
+ return( 0L );
+ }
+
+} // AnsiToUnicode()
+
+
+
+//+---------------------------------------------------------------------------------------
+//
+// Function: main
+//
+// Synopsis: Run the CFMEx DRT. All tests are in the CTest object. These
+// tests are simply called sequentially.
+//
+// Inputs: (argc) the count of arguments
+// (argv) the arguments. See DisplayHelp() for a description.
+//
+// Outputs: 0 if completely successful
+// 1 if help was displayed
+// 2 if a test failed
+//
+// Effects: None
+//
+//+---------------------------------------------------------------------------------------
+
+
+int main( int argc, char** argv )
+{
+
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ // Test statistics.
+
+ int nTotalTests = 0;
+ int nTestsFailed = 0;
+
+ // The original and final directories (in ANSI) of the link source file.
+
+ CHAR* szOriginalDirectory = NULL;
+ CHAR* szFinalDirectory = NULL;
+
+ // Objects representing the original and final directories.
+
+ CDirectory cDirectoryOriginal;
+ CDirectory cDirectoryFinal;
+
+ // The test engine.
+
+ CTest cTest;
+
+ int index = 0;
+
+
+ // --------------
+ // Opening Banner
+ // --------------
+
+ printf( "\n"
+ "*** CreateFileMonikerEx DRT ***\n"
+ "(use \"cfmex -?\" for help)\n"
+ "\n" );
+
+
+ // ------------------------------
+ // Handle command-line parameters
+ // ------------------------------
+
+
+ for( index = 1; index < argc; index++ )
+ {
+ // The first character of an argument should be an "-" or "/"
+ // (they are interchangable).
+
+ if( ( ( argv[index][0] != '-' )
+ &&
+ ( argv[index][0] != '/' )
+ )
+ ||
+ ( strlen( &argv[index][0] ) < 2 ) // Must have more than '-' & an option.
+ )
+ {
+ printf( "Invalid argument ignored: %s\n", argv[ index ] );
+ continue;
+ }
+
+
+ // Switch based on the first character (which defines the option).
+
+ switch( argv[index][1] )
+ {
+ // Help requested
+
+ case '?':
+ DisplayHelp();
+ exit( 1 );
+
+ // An original directory is specified.
+
+ case 'o':
+ case 'O':
+
+ // Verify the specified path length.
+
+ if( strlen( &argv[index][2] ) > MAX_PATH )
+ {
+ printf( "Path is too long, ignored: %s\n", &argv[index][2] );
+ break; // From the switch
+ }
+
+ szOriginalDirectory = &argv[index][2];
+ break; // From the switch
+
+ // A final directory is specified
+
+ case 'f':
+ case 'F':
+
+ if( strlen( &argv[index][2] ) > MAX_PATH )
+ {
+ printf( "Path is too long, ignored: %s\n", &argv[index][2] );
+ break; // From the switch
+ }
+
+ szFinalDirectory = &argv[index][2];
+ break; // From the switch
+
+ // Invalid argument.
+
+ default:
+
+ printf( "Invalid option ignored: \"-%c\"\n", argv[index][1] );
+ break;
+ }
+ }
+
+ // --------------
+ // Initialization
+ // --------------
+
+ // Initialize COM
+
+ CoInitialize( NULL );
+
+
+ // Initialize the CDirectory and CTest objects. If no original/final
+ // directory was specified above, CDirectory will create a default
+ // (based on %TEMP%).
+
+ if( !cDirectoryOriginal.Initialize( szOriginalDirectory ) )
+ EXIT( L"Could not initialize cDirectoryOriginal" );
+
+ if( !cDirectoryFinal.Initialize( szFinalDirectory ) )
+ EXIT( L"Could not initialize cDirectoryFinal" );
+
+ if( !cTest.Initialize( cDirectoryOriginal, cDirectoryFinal ) )
+ EXIT( L"Could not initialize CTest" );
+
+
+ // Show the end result.
+
+ wprintf( L"Link sources will be created in \"%s\" (%s)\n",
+ cDirectoryOriginal.GetDirectoryName(),
+ cDirectoryOriginal.GetFileSystemName() );
+ wprintf( L"and will be moved to \"%s\" (%s)\n",
+ cDirectoryFinal.GetDirectoryName(),
+ cDirectoryFinal.GetFileSystemName() );
+
+
+ // ---------
+ // Run Tests
+ // ---------
+
+ RUN_TEST( GetOversizedBindOpts() );
+ RUN_TEST( GetUndersizedBindOpts() );
+ RUN_TEST( SetOversizedBindOpts() );
+ RUN_TEST( SetUndersizedBindOpts() );
+ RUN_TEST( CreateFileMonikerEx() );
+ RUN_TEST( GetDisplayName() );
+ RUN_TEST( GetTimeOfLastChange() );
+ RUN_TEST( ComposeWith() );
+ RUN_TEST( IPersist() );
+ RUN_TEST( BindToStorage() );
+ RUN_TEST( BindToObject() );
+ RUN_TEST( DeleteLinkSource( 0 )); // Timeout immediately.
+ RUN_TEST( DeleteLinkSource( 200 )); // Timeout in multi-threaded search
+ RUN_TEST( DeleteLinkSource( INFINITE )); // Don't timeout
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ // Show test results.
+
+ wprintf( L"\n\nTesting complete:\n"
+ L" Total Tests = %d\n"
+ L" Failed = %d\n",
+ nTotalTests, nTestsFailed );
+
+ // Free COM
+
+ CoUninitialize();
+
+ return( nTestsFailed ? 2 : 0 );
+}
diff --git a/private/oleutest/cfmex/cfmex.hxx b/private/oleutest/cfmex/cfmex.hxx
new file mode 100644
index 000000000..5f1f6f488
--- /dev/null
+++ b/private/oleutest/cfmex/cfmex.hxx
@@ -0,0 +1,31 @@
+
+//+==============================================================================
+//
+// File: CFMEx.hxx
+//
+// Purpose: Provide global definitions and function prototypes
+// for the CreateFileMonikerEx DRT.
+//
+//+==============================================================================
+
+#ifndef _CFMEX_HXX_
+#define _CFMEX_HXX_
+
+// The size of a buffer which will hold a path depends on the size
+// of the characters.
+
+#define MAX_ANSI_PATH MAX_PATH
+#define MAX_UNICODE_PATH ( MAX_PATH * sizeof( WCHAR ))
+
+// Function prototypes.
+
+DWORD AnsiToUnicode( const CHAR * szAnsi,
+ WCHAR * wszUnicode,
+ int cbUnicodeMax );
+
+DWORD UnicodeToAnsi( const WCHAR * wszUnicode,
+ CHAR * szAnsi,
+ int cbAnsiMax );
+
+
+#endif // _CFMEX_HXX_
diff --git a/private/oleutest/cfmex/cmoniker.cxx b/private/oleutest/cfmex/cmoniker.cxx
new file mode 100644
index 000000000..7a9c555a7
--- /dev/null
+++ b/private/oleutest/cfmex/cmoniker.cxx
@@ -0,0 +1,1684 @@
+
+//+=======================================================================
+//
+// File: CMoniker.cxx
+//
+// Purpose: Define the CMoniker class.
+//
+// This class provides for all handling of monikers in
+// the CreateFileMonikerEx DRT. Not only does it maintain
+// a file moniker, it also maintains the represented link
+// source file, and a bind context.
+//
+//+=======================================================================
+
+// --------
+// Includes
+// --------
+
+#define _DCOM_ // Allow DCOM extensions (e.g., CoInitializeEx).
+
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <wtypes.h>
+#include <oaidl.h>
+#include <dsys.h>
+#include <olecairo.h>
+#include "CFMEx.hxx"
+#include "CMoniker.hxx"
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: CMoniker::CMoniker
+//
+// Synopsis: Simply initialize all member variables.
+//
+// Inputs: None.
+//
+// Outputs: N/A
+//
+// Effects: Members are defaulted/initialized.
+//
+//+-------------------------------------------------------------------------
+
+
+CMoniker::CMoniker()
+{
+ *m_wszSystemTempPath = L'\0';
+ *m_wszTemporaryStorage = L'\0';
+ m_pIMoniker = NULL;
+ m_pIBindCtx = NULL;
+ m_pIStorage = NULL;
+ *m_wszErrorMessage = L'\0';
+ m_dwTrackFlags = 0L;
+ m_hkeyLinkTracking = NULL;
+ m_hr = 0L;
+ m_bSuppressErrorMessages = FALSE;
+ m_pcDirectoryOriginal = NULL;
+ m_pcDirectoryFinal = NULL;
+
+ return;
+
+} // CMoniker::CMoniker()
+
+
+//+--------------------------------------------------------------------------
+//
+// Function: CMoniker::~CMoniker
+//
+// Synopsis: Release any COM objects.
+//
+// Inputs: N/A
+//
+// Outputs: N/A
+//
+// Effects: All COM objects are released.
+//
+//+--------------------------------------------------------------------------
+
+
+CMoniker::~CMoniker()
+{
+ if( m_pIMoniker )
+ {
+ m_pIMoniker->Release();
+ m_pIMoniker = NULL;
+ }
+
+ if( m_pIBindCtx )
+ {
+ m_pIBindCtx->Release();
+ m_pIBindCtx = NULL;
+ }
+
+ if( m_pIStorage )
+ {
+ m_pIStorage->Release();
+ m_pIStorage = NULL;
+ }
+
+ return;
+
+} // CMoniker::~CMoniker()
+
+
+//+-----------------------------------------------------------------------
+//
+// Function: CMoniker::Initialize
+//
+// Synopsis: Keep pointers to the CDirectory objects passed in.
+//
+// Inputs: A CDirectory object for the original link source file location
+// A CDirectory object for the final location
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: The member CDirectory objects are set.
+//
+//+-----------------------------------------------------------------------
+
+
+BOOL CMoniker::Initialize( const CDirectory& cDirectoryOriginal,
+ const CDirectory& cDirectoryFinal )
+{
+ m_hr = S_OK;
+
+ m_pcDirectoryOriginal = &cDirectoryOriginal;
+ m_pcDirectoryFinal = &cDirectoryFinal;
+
+ return( TRUE ); // Success
+
+} // CMoniker::Initialize()
+
+
+//+-----------------------------------------------------------------------------
+//
+// Function: CMoniker::CreateFileMonikerEx
+//
+// Synopsis: Create a tracking file moniker. But before doing so, initialize
+// the Bind Context, and create a link source file.
+//
+// Inputs: Track Flags (from the TRACK_FLAGS defines)
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: The member bind context is initialized, and a link
+// source file is created.
+//
+//+-----------------------------------------------------------------------------
+
+
+BOOL CMoniker::CreateFileMonikerEx( DWORD dwTrackFlags )
+{
+
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ // Assume failure
+ BOOL bSuccess = FALSE;
+
+ // -----
+ // Begin
+ // -----
+
+ // Initialize the error code.
+
+ m_hr = S_OK;
+
+ // Free any existing IMoniker.
+
+ if( m_pIMoniker )
+ {
+ m_pIMoniker->Release();
+ m_pIMoniker = NULL;
+ }
+
+ // Initialize the bind context.
+
+ if( !InitializeBindContext() )
+ EXIT( L"Could not initialize the bind context" );
+
+ // Create a root storage for use as a link source.
+
+ if( !CreateTemporaryStorage() )
+ EXIT( L"Could not create temporary Storage" );
+
+ // Create a tracking File Moniker on that root storage.
+
+ m_hr = ::CreateFileMonikerEx( dwTrackFlags, m_wszTemporaryStorage, &m_pIMoniker );
+ EXIT_ON_FAILED( L"Failed CreateFileMonikerEx" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::CreateFileMonikerEx" );
+ return( bSuccess );
+
+} // CMoniker::CreateFileMonikerEx()
+
+
+//+---------------------------------------------------------------------
+//
+// Function: CMoniker::SaveDeleteLoad
+//
+// Synopsis: This function exercises a moniker's IPersistStream interface.
+// It creates saves the member moniker's persistent state to
+// a stream, deletes the moniker, and then re-creates it
+// using CreateFileMoniker (no Ex, so it's not a tracking
+// file moniker). It then re-loads the original moniker's
+// persistent state.
+//
+// Inputs: None.
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: The member moniker is deleted, re-created, and re-loaded.
+//
+//+---------------------------------------------------------------------
+
+
+BOOL CMoniker::SaveDeleteLoad()
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ // Assume failure
+ BOOL bSuccess = FALSE;
+ HRESULT hr = E_FAIL;
+
+ IStream* pStream = NULL;
+ IPersistStream* pIPersistStream = NULL;
+ LARGE_INTEGER li;
+ ULARGE_INTEGER uli;
+
+ // -----
+ // Begin
+ // -----
+
+ // Initialize the error code.
+
+ m_hr = S_OK;
+
+ // Verify that we have a member moniker.
+
+ if( !m_pIMoniker )
+ EXIT( L"Attempt to run SaveDeleteLoad test without an existing file moniker" );
+
+
+ // ------------------------
+ // Save the moniker's state
+ // ------------------------
+
+ // Get the moniker's IPersistStream interface
+
+ m_hr = m_pIMoniker->QueryInterface( IID_IPersistStream, (void **) &pIPersistStream );
+ EXIT_ON_FAILED( L"Failed 1st IMoniker::QueryInterface(IPersistStream)" );
+
+ // Create a stream
+
+ hr = CreateStreamOnHGlobal( NULL, // Auto alloc
+ TRUE, // Delete on release
+ &pStream );
+ EXIT_ON_FAILED( L"Failed CreateStreamOnHGlobal()" );
+
+ // Save the moniker's state to this stream.
+
+ hr = pIPersistStream->Save( pStream, TRUE /* Clear dirty*/ );
+ EXIT_ON_FAILED( L"Failed IPersistStream::Save()" );
+
+ // ------------------
+ // Delete the moniker
+ // ------------------
+
+ // Release all interfaces for the moniker.
+
+ m_pIMoniker->Release();
+ pIPersistStream->Release();
+
+ m_pIMoniker = NULL;
+ pIPersistStream = NULL;
+
+ // --------------------
+ // Create a new moniker
+ // --------------------
+
+ // Create a new moniker, using the non-Ex version of the function.
+
+ m_hr = ::CreateFileMoniker( m_wszTemporaryStorage, &m_pIMoniker );
+ EXIT_ON_FAILED( L"Failed CreateFileMoniker()" );
+
+ // --------------------
+ // Load the new moniker
+ // --------------------
+
+ // Get the IPersisStream interface
+
+ m_hr = m_pIMoniker->QueryInterface( IID_IPersistStream, (void **) &pIPersistStream );
+ EXIT_ON_FAILED( L"Failed 2nd IMoniker::QueryInterface(IPersistStream)" );
+
+ // Re-seek the stream to the beginning.
+
+ li.LowPart = li.HighPart = 0L;
+ hr = pStream->Seek( li, STREAM_SEEK_SET, &uli );
+ EXIT_ON_FAILED( L"Failed IStream::Seek()" );
+ if( uli.LowPart || uli.HighPart ) EXIT( L"Incorrect IStream::Seek()" );
+
+ // Re-load the moniker from the stream.
+
+ m_hr = pIPersistStream->Load( pStream );
+ EXIT_ON_FAILED( L"Failed IPersistStream::Load()" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ // Clean up the stream and the IPersistStream interface.
+
+ if( pStream )
+ pStream->Release;
+
+ if( pIPersistStream )
+ pIPersistStream->Release();
+
+
+ DisplayErrors( bSuccess, L"CMoniker::SaveDeleteLoad()" );
+ return( bSuccess );
+
+} // CMoniker::SaveDeleteLoad()
+
+
+//+------------------------------------------------------------------
+//
+// Function: CMoniker::ComposeWith
+//
+// Synopsis: Compose a tracking moniker with a non-tracking moniker
+// on the right. (The resulting moniker should be tracking,
+// but this is not relevant to this function; that is, whether
+// or not the composed moniker is tracking, this function will
+// succeed.)
+//
+// Inputs: None.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+// Effects: The member moniker is deleted, then recreated.
+//
+//+------------------------------------------------------------------
+
+BOOL CMoniker::ComposeWith()
+{
+
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ IMoniker* pmkrFirst = NULL;
+ IMoniker* pmkrSecond = NULL;
+ HRESULT hr = E_FAIL;
+
+ WCHAR wszDirectoryName[ MAX_PATH + sizeof( L'\0' ) ];
+ WCHAR* wszFileName = NULL;
+
+ // -----
+ // Begin
+ // -----
+
+ // Initiailize the error code.
+
+ m_hr = S_OK;
+
+ // If we have a moniker already, delete it.
+
+ if( m_pIMoniker )
+ {
+ m_pIMoniker->Release();
+ m_pIMoniker = NULL;
+ }
+
+ // Verify we already have a link source file created.
+
+ if( !wcslen( m_wszTemporaryStorage ) )
+ EXIT( L"Attempt to use ComposeWith without first creating a link source.\n" );
+
+ // -----------------------------------------------
+ // Create a tracking and non-tracking file moniker
+ // -----------------------------------------------
+
+ // Parse the storage's filename into a path and a file ...
+ // for example, "C:\Temp\file.tmp" would become
+ // "C:\Temp" and "file.tmp".
+ //
+ // First, make a copy of the storage's complete path name,
+ // then replace the last '\\' with a '\0', thus creating two
+ // strings.
+
+ wcscpy( wszDirectoryName, m_wszTemporaryStorage );
+ wszFileName = wcsrchr( wszDirectoryName, L'\\' );
+ *wszFileName = L'\0';
+ wszFileName++;
+
+ // Create a tracking file moniker using the directory name.
+
+ m_hr = ::CreateFileMonikerEx( 0L, wszDirectoryName, &pmkrFirst );
+ EXIT_ON_FAILED( L"Failed 1st CreateFileMoniker()" );
+
+ // Create a non-tracking file moniker using the file name.
+
+ m_hr = ::CreateFileMoniker( wszFileName, &pmkrSecond );
+ EXIT_ON_FAILED( L"Failed 2nd CreateFileMoniker()" );
+
+ // -------
+ // Compose
+ // -------
+
+ // Compose the directory name moniker (on the left) with the file name moniker
+ // (on the right). Put the result in the member moniker.
+
+ m_hr = pmkrFirst->ComposeWith( pmkrSecond, TRUE, &m_pIMoniker );
+ EXIT_ON_FAILED( L"Failed IMoniker::ComposeWith" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ // Clean up the intermediary monikers.
+
+ if( pmkrFirst )
+ pmkrFirst->Release();
+
+ if( pmkrSecond )
+ pmkrSecond->Release();
+
+ DisplayErrors( bSuccess, L"CMoniker::ComposeWith()" );
+ return( bSuccess );
+
+} // CMoniker::ComposeWith()
+
+
+//+--------------------------------------------------------------------
+//
+// Function: CMoniker::CreateTemporaryStorage
+//
+// Synopsis: This function creates a Structured Storage
+// in the directory identified by m_cDirectoryOriginal.
+// The name of the file is randomly generated by the system,
+// but begins with "MKR" and has the extension ".tmp".
+//
+// Inputs: None.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+// Effects: Stores the Structure Storage's name in
+// m_wszTemporaryStorageName, and releases m_pIStorage if
+// it is currently set.
+//
+//+--------------------------------------------------------------------
+
+
+BOOL CMoniker::CreateTemporaryStorage()
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ DWORD dwError = 0L;
+ UINT nError = 0;
+
+
+ // -----
+ // Begin
+ // -----
+
+ m_hr = S_OK;
+
+ // Delete any existing storage.
+
+ if( wcslen( m_wszTemporaryStorage ))
+ {
+ if( !DeleteTemporaryStorage() )
+ EXIT( L"Could not delete the existing temporary storage" );
+ }
+
+ // Generate a temporary filename.
+
+ nError = GetTempFileName( m_pcDirectoryOriginal->GetDirectoryName(),
+ L"MKR", // Prefix string.
+ 0, // Generate random number,
+ m_wszTemporaryStorage );
+ if( nError == 0 )
+ {
+ m_hr = (HRESULT) GetLastError();
+ EXIT( L"Failed GetTempFileName()" );
+ }
+
+ // Create a root Storage.
+
+ m_hr = StgCreateDocfile( m_wszTemporaryStorage,
+ STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT,
+ 0L, // Reserved
+ &m_pIStorage );
+ EXIT_ON_FAILED( L"Failed StgCreateDocfile()" );
+
+
+ // Release the storage.
+
+ m_pIStorage->Release();
+ m_pIStorage = NULL;
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+
+ DisplayErrors( bSuccess, L"CMoniker::CreateTemporaryStorage()" );
+ return( bSuccess );
+
+} // CMoniker::CreateTemporaryStorage()
+
+
+//+-------------------------------------------------------------------------
+//
+// Function: CMoniker::RenameTemporaryStorage
+//
+// Synopsis: Rename the link source file (who's name is in m_wszTemporaryStorage)
+// to the m_cDirectoryFinal directory, with a new name.
+// The current name is "MKR#.tmp" (where "#" is a random number
+// generated by the system), and the new name is "RKM#.tmp" (where
+// "#" is the same random number). (We must rename the base file
+// name, rather than its extension, because otherwise the default
+// link-tracking would fail (it would only score the renamed file
+// a 32 - by matching the file extension the score is 40.)
+//
+// Inputs: None.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+// Effects: The link source file is renamed, and it's new name is
+// put into m_wszTemporaryStorage.
+//
+//+-------------------------------------------------------------------------
+
+
+BOOL CMoniker::RenameTemporaryStorage()
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+ int nError = 0;
+
+ WCHAR wszNewName[ MAX_PATH + sizeof( L'\0' ) ];
+ WCHAR* wszOldFileName;
+ WCHAR wszNewFileName[ MAX_PATH + sizeof( L'\0' ) ];
+
+ char szOldName[ MAX_PATH + sizeof( L'\0' ) ];
+ char szNewName[ MAX_PATH + sizeof( L'\0' ) ];
+
+
+ // -----
+ // Begin
+ // -----
+
+ m_hr = S_OK;
+
+ // Verify that we already have a link source created.
+
+ if( !wcslen( m_wszTemporaryStorage ))
+ EXIT( L"No temporary storage to rename." );
+
+ // Locate the file name within the complete path.
+ // (E.g., find the "foo.txt" in "C:\TEMP\foot.txt".)
+
+ wszOldFileName = wcsrchr( m_wszTemporaryStorage, L'\\' );
+ if( !wszOldFileName )
+ EXIT( L"Could not extract old file name from temporary storage name\n" );
+ wszOldFileName++; // Get past the '\\'
+
+ // Generate the new file name (change "MKR" to "RKM").
+
+ wcscpy( wszNewFileName, wszOldFileName );
+
+ wszNewFileName[0] = L'R';
+ wszNewFileName[1] = L'K';
+ wszNewFileName[2] = L'M';
+
+ // Generate the complete path spec of the new file.
+
+ wcscpy( wszNewName, m_pcDirectoryFinal->GetDirectoryName() );
+ wcscat( wszNewName, wszNewFileName );
+
+
+ // Convert the new and old file names to ANSI.
+
+ if( m_hr = UnicodeToAnsi( m_wszTemporaryStorage, szOldName, sizeof( szOldName )) )
+ {
+ EXIT( L"Could not convert convert Unicode to Ansi for old name" );
+ }
+
+ if( m_hr = UnicodeToAnsi( wszNewName, szNewName, sizeof( szNewName )) )
+ {
+ EXIT( L"Could not convert convert Unicode to Ansi for new name" );
+ }
+
+
+ // Rename the file.
+
+ nError = rename( szOldName, szNewName );
+ if( nError )
+ {
+ m_hr = (HRESULT) errno;
+ EXIT( L"Failed rename()" );
+ }
+
+ // Record the new name.
+
+ wcscpy( m_wszTemporaryStorage, wszNewName );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::RenameTemporaryStorage()" );
+ return( bSuccess );
+
+} // CMoniker::RenameTemporaryStorage()
+
+
+//+--------------------------------------------------------------------
+//
+// Function: CMoniker::DeleteTemporaryStorage
+//
+// Synopsis: Delete the temporary storage that this object uses
+// as a link source for the moniker. The name of the
+// storage is in m_wszTemporaryStorage.
+//
+// Inputs: None.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+// Effects: The link source file is deleted, and m_wszTemporaryStorage
+// is set to a NULL string.
+//
+//+--------------------------------------------------------------------
+
+
+BOOL CMoniker::DeleteTemporaryStorage()
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ int nError = 0;
+ CHAR szTemporaryStorage[ MAX_PATH + sizeof( '\0' ) ];
+
+ // -----
+ // Begin
+ // -----
+
+ m_hr = S_OK;
+
+ // Don't do anything if we have no file (don't report an
+ // error either; the caller wants the file deleted, and it's
+ // already not there).
+
+ if( wcslen( m_wszTemporaryStorage ))
+ {
+
+ // Get the file name in ANSI.
+
+ if( m_hr = UnicodeToAnsi( m_wszTemporaryStorage, szTemporaryStorage, sizeof( szTemporaryStorage )))
+ EXIT( L"Could not convert unicode path to ANSI path" );
+
+ // Delete the file.
+
+ nError = unlink( szTemporaryStorage );
+ if( nError )
+ {
+ m_hr = (HRESULT) errno;
+ EXIT( L"Failed unlink()" );
+ }
+
+ // Clear the file name.
+
+ wcscpy( m_wszTemporaryStorage, L"" );
+ }
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::DeleteTemporaryStorage()" );
+ return( bSuccess );
+
+} // CMoniker::DeleteTemporaryStorage()
+
+
+
+
+//+-----------------------------------------------------------------
+//
+// Function: CMoniker::Reduce
+//
+// Synopsis: Perform a IMoniker::Reduce on the member moniker.
+//
+// Inputs: - Number of ticks until the deadline for completion of
+// the operation.
+// - A buffer into which to put the reduced IMoniker*
+// (may be NULL).
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+-----------------------------------------------------------------
+
+
+BOOL CMoniker::Reduce( DWORD dwDelay, IMoniker** ppmkReturn )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+ IMoniker* pmkReduced = NULL;
+ BIND_OPTS2 bind_opts;
+ bind_opts.cbStruct = sizeof( BIND_OPTS2 );
+
+ // -----
+ // Begin
+ // -----
+
+ m_hr = S_OK;
+
+ // ----------
+ // Initialize
+ // ----------
+
+ // Initialize the return buffer, if extant.
+
+ if( ppmkReturn )
+ *ppmkReturn = NULL;
+
+ // Validate our state.
+
+ if( !m_pIMoniker )
+ EXIT( L"No moniker exists to be reduced" );
+
+ // ----------------
+ // Set the deadline
+ // ----------------
+
+ // Get the BIND_OPTS from the bind context.
+
+ m_hr = m_pIBindCtx->GetBindOptions( (LPBIND_OPTS) &bind_opts );
+ EXIT_ON_FAILED( L"Failed IBindCtx::GetBindOptions" );
+
+ // Determine what the tick count of the deadline is.
+
+ if( dwDelay == INFINITE )
+ {
+ bind_opts.dwTickCountDeadline = 0;
+ }
+ else
+ {
+ bind_opts.dwTickCountDeadline = GetTickCount() + dwDelay;
+
+ // Make sure the resulting tick count is not 0 (indicating no
+ // deadline).
+
+ if( bind_opts.dwTickCountDeadline == 0 )
+ bind_opts.dwTickCountDeadline++;
+ }
+
+ // Put the resulting BIND_OPTS back into the bind context.
+
+ m_hr = m_pIBindCtx->SetBindOptions( (LPBIND_OPTS) &bind_opts );
+ EXIT_ON_FAILED( L"Failed IBindCtx::SetBindOptions" );
+
+
+ // ------------------
+ // Reduce the Moniker
+ // ------------------
+
+ m_hr = m_pIMoniker->Reduce( m_pIBindCtx,
+ MKRREDUCE_ALL,
+ NULL,
+ &pmkReduced );
+ EXIT_ON_FAILED( L"Failed IMoniker::Reduce" );
+
+ // Return the reduced moniker to the caller (if so requested).
+
+ if( ppmkReturn )
+ {
+ // Transfer responsibility for the release to the caller.
+ *ppmkReturn = pmkReduced;
+ pmkReduced = NULL;
+ }
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::Reduce()" );
+
+ if( pmkReduced )
+ pmkReduced->Release();
+
+ return( bSuccess );
+
+} // CMoniker::Reduce()
+
+
+
+//+----------------------------------------------------------------------
+//
+// Function: CMoniker::GetDisplayName
+//
+// Synopsis: Get the moniker's display name.
+//
+// Inputs: A Unicode buffer for the display name, and (optionally)
+// a moniker from which to get the display name. If such
+// a moniker is not provided by the caller, then the member
+// moniker is used.
+//
+// The unicode buffer must be long enough for MAX_PATH characters
+// and a terminating NULL.
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+----------------------------------------------------------------------
+
+
+BOOL CMoniker::GetDisplayName( WCHAR * wszDisplayName, IMoniker* pmnkCaller )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ WCHAR* wszReturnedDisplayName = NULL;
+ IMoniker* pmnk = NULL;
+
+ // -----
+ // Begin
+ // -----
+
+ m_hr = NOERROR;
+
+ // Determine which moniker to use, the caller-specified one or
+ // the member one.
+
+ if( pmnkCaller != NULL )
+ pmnk = pmnkCaller;
+ else
+ pmnk = m_pIMoniker;
+
+ if( !pmnk )
+ EXIT( L"Attempt to GetDisplayName on NULL moniker" );
+
+ // Get the display name from the moniker.
+
+ m_hr = pmnk->GetDisplayName( m_pIBindCtx,
+ NULL,
+ &wszReturnedDisplayName );
+ EXIT_ON_FAILED( L"Failed IMoniker::GetDisplayName()" );
+
+ if( wcslen( wszReturnedDisplayName ) > MAX_UNICODE_PATH )
+ EXIT( L"IMoniker::GetDisplayName() returned a path which was too long" );
+
+ // Copy the display name into the caller's buffer, and free it.
+
+ wcscpy( wszDisplayName, wszReturnedDisplayName );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ if( wszReturnedDisplayName )
+ {
+ CoTaskMemFree( wszReturnedDisplayName );
+ wszReturnedDisplayName = NULL;
+ }
+
+ DisplayErrors( bSuccess, L"CMoniker::GetDisplayName()" );
+ return( bSuccess );
+
+} // CMoniker::GetDisplayName()
+
+
+//+-------------------------------------------------------------
+//
+// Function: CMoniker::InitializeBindContext
+//
+// Synopsis: Create a new bind context, and store it
+// in a member pointer.
+//
+// Inputs: None.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+// Effects: Updates m_pIBindCtx.
+//
+//+-------------------------------------------------------------
+
+
+
+BOOL CMoniker::InitializeBindContext( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ // -----
+ // Begin
+ // -----
+
+ m_hr = S_OK;
+
+ // Release the old bind context if we have one.
+
+ if( m_pIBindCtx )
+ m_pIBindCtx->Release();
+
+ // Create the new bind context.
+
+ m_hr = CreateBindCtx( 0L, &m_pIBindCtx );
+ EXIT_ON_FAILED( L"Failed CreateBindCtx()" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::InitializeBindContext()" );
+ return( bSuccess );
+
+} // CMoniker::InitializeBindContext()
+
+
+//+----------------------------------------------------------------
+//
+// Function: CMoniker::GetTimeOfLastChange
+//
+// Synopsis: Request the time-of-last-change from our member
+// moniker.
+//
+// Inputs: A buffer into which to put the FILETIME.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+----------------------------------------------------------------
+
+
+BOOL CMoniker::GetTimeOfLastChange( FILETIME* pft )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ // -----
+ // Begin
+ // -----
+
+ m_hr = S_OK;
+
+ // Validate our state.
+
+ if( !m_pIMoniker )
+ EXIT( L"Cannot GetTimeOfLastChange on a NULL moniker" );
+
+ // Get the time from the moniker.
+
+ m_hr = m_pIMoniker->GetTimeOfLastChange( m_pIBindCtx,
+ NULL,
+ pft );
+ EXIT_ON_FAILED( L"Failed IMoniker::GetTimeOfLastChange()" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::GetTimeOfLastChange()" );
+ return( bSuccess );
+
+} // CMoniker::GetTimeOfLastChange()
+
+
+
+//+------------------------------------------------------------------------
+//
+// Function: CMoniker::BindToStorage
+//
+// Synopsis: Bind our member moniker to its Structured Storage object.
+//
+// Inputs: None.
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+------------------------------------------------------------------------
+
+
+BOOL CMoniker::BindToStorage()
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+ BIND_OPTS2 bind_opts;
+ bind_opts.cbStruct = sizeof( BIND_OPTS2 );
+
+ // -----
+ // Begin
+ // -----
+
+ m_hr = S_OK;
+
+ // Validate our state.
+
+ if( !m_pIMoniker )
+ EXIT( L"Cannot GetTimeOfLastChange on a NULL moniker" );
+
+ // Release the IStorage interface if we have one.
+
+ if( m_pIStorage )
+ {
+ m_pIStorage->Release();
+ m_pIStorage = NULL;
+ }
+
+ // Get the bind_opts and set the flags for StgOpenStorage.
+
+ m_hr = m_pIBindCtx->GetBindOptions( (LPBIND_OPTS) &bind_opts );
+ EXIT_ON_FAILED( L"Failed IBindCtx::GetBindOptions" );
+
+ bind_opts.grfMode = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT;
+
+ m_hr = m_pIBindCtx->SetBindOptions( (LPBIND_OPTS) &bind_opts );
+ EXIT_ON_FAILED( L"Failed IBindCtx::SetBindOptions" );
+
+
+ // Bind to the storage.
+
+ m_hr = m_pIMoniker->BindToStorage( m_pIBindCtx,
+ NULL,
+ IID_IStorage,
+ (void **) &m_pIStorage );
+ EXIT_ON_FAILED( L"Failed IMoniker::BindToStorage()" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ // Release the Storage if we got it.
+
+ if( m_pIStorage )
+ {
+ m_pIStorage->Release();
+ m_pIStorage = NULL;
+ }
+
+ DisplayErrors( bSuccess, L"CMoniker::BindToStorage()" );
+ return( bSuccess );
+
+} // CMoniker::BindToStorage()
+
+
+//+-------------------------------------------------------------------
+//
+// Function: CMoniker::BindToObject
+//
+// Synopsis: Bind to our member moniker's object.
+//
+// Inputs: None.
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+// Notes: Since the member moniker represents a storage with no
+// associated server, BindToObject will fail. We will
+// consider it a success if the failure is do to an
+// object-related problem, rather than a Storage-related
+// problem.
+//
+//+-------------------------------------------------------------------
+
+BOOL CMoniker::BindToObject()
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+ IUnknown* pUnk = NULL;
+
+ // -----
+ // Begin
+ // -----
+
+ m_hr = S_OK;
+
+ // Validate our state.
+
+ if( !m_pIMoniker )
+ EXIT( L"Cannot bind to an object with a NULL moniker" );
+
+ // Bind to the object.
+
+ m_hr = m_pIMoniker->BindToObject( m_pIBindCtx,
+ NULL,
+ IID_IUnknown,
+ (void **) &pUnk );
+
+ // If the bind succeeded, or failed for a valid reason,
+ // then return Success to the caller.
+
+ if( SUCCEEDED( m_hr )
+ ||
+ ( m_hr = MK_E_INVALIDEXTENSION ) // No handler for ".tmp" files.
+ )
+ {
+ bSuccess = TRUE;
+ }
+ else
+ {
+ EXIT( L"Failed BindToObject" );
+ }
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ // If we got an IUnknown interface on the Bind, release it.
+
+ if( pUnk )
+ {
+ pUnk->Release();
+ pUnk = NULL;
+ }
+
+ DisplayErrors( bSuccess, L"CMoniker::BindToObject()" );
+ return( bSuccess );
+
+} // CMoniker::BindToObject()
+
+
+//+-------------------------------------------------------------------
+//
+// Function: CMoniker::GetTemporaryStorageTime
+//
+// Synopsis: Get the time from the link source file
+// (identified by m_wszTemporaryStorage).
+//
+// Inputs: A buffer in which to put the FILETIME structure.
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+-------------------------------------------------------------------
+
+
+BOOL CMoniker::GetTemporaryStorageTime( FILETIME * pft)
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+ HANDLE hFile = NULL;
+
+ // -----
+ // Begin
+ // -----
+
+ m_hr = NOERROR;
+
+ // Get a handle to the file.
+
+ hFile = CreateFile( m_wszTemporaryStorage, // File name
+ GENERIC_READ, // Desired access
+ FILE_SHARE_READ, // Share mode
+ NULL, // Security attributes
+ OPEN_EXISTING, // Creation distribution
+ 0L, // Flags & Attributes
+ NULL ); // hTemplateFile
+ if( hFile == NULL )
+ {
+ m_hr = (HRESULT) GetLastError();
+ EXIT( L"Failed call to CreateFile()" );
+ }
+
+ // Get the time on the file.
+
+ if( !GetFileTime( hFile, // File to check
+ NULL, // Create Time
+ NULL, // Access Time
+ pft ) // Write Time
+ )
+ {
+ m_hr = (HRESULT) GetLastError();
+ EXIT( L"Failed call to GetFileTime()" );
+ }
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ // Close the file if we opened it.
+
+ if( hFile )
+ {
+ CloseHandle( hFile );
+ hFile = NULL;
+ }
+
+
+ DisplayErrors( bSuccess, L"CMoniker::GetTemporaryStorageTime()" );
+ return( bSuccess );
+
+} // CMoniker::GetTemporaryStorageTime()
+
+
+//+------------------------------------------------------------------------
+//
+// Function: CMoniker::TouchTemporaryStorage
+//
+// Synopsis: Set the Access time on the link source file.
+//
+// Inputs: None.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+// Effects: The link source file (identified by m_wszTemporaryStorage)
+// has its Access time set to the current time.
+//
+//+------------------------------------------------------------------------
+
+
+BOOL CMoniker::TouchTemporaryStorage( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+ HANDLE hFile = NULL;
+ STATSTG statStorage;
+ FILETIME ftNow;
+
+ // -----
+ // Begin
+ // -----
+
+ m_hr = NOERROR;
+
+ // Open the root Storage.
+
+ m_hr = StgOpenStorage( m_wszTemporaryStorage,
+ NULL,
+ STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT,
+ NULL,
+ 0L,
+ &m_pIStorage );
+ EXIT_ON_FAILED( L"Failed StgOpenStorage()" );
+
+ // Get the current time.
+
+ m_hr = CoFileTimeNow( &ftNow );
+ EXIT_ON_FAILED( L"Failed CoFileTimeNow()" );
+
+ // Set the access time
+
+ m_pIStorage->SetElementTimes( NULL, // Set the storage itself
+ NULL, // Create time
+ NULL, // Access time
+ &ftNow );
+ EXIT_ON_FAILED( L"Failed IStorage::SetTimes()" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ // If we got the storage, release it.
+
+ if( m_pIStorage )
+ {
+ m_pIStorage->Release();
+ m_pIStorage = NULL;
+ }
+
+ DisplayErrors( bSuccess, L"CMoniker::TouchTemporaryStorage()" );
+ return( bSuccess );
+
+} // CMoniker::TouchTemporaryStorage()
+
+
+
+#ifdef _FUTURE_
+
+/*
+BOOL CMoniker::OpenLinkTrackingRegistryKey()
+{
+
+ BOOL bSuccess = FALSE;
+ DWORD dwDisposition = 0L;
+ long lResult = 0L;
+
+ m_hr = S_OK;
+
+ lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+ OLETRACKING_KEY,
+ 0L,
+ KEY_ALL_ACCESS,
+ &m_hkeyLinkTracking
+ );
+
+ if( lResult != ERROR_SUCCESS
+ &&
+ lResult != ERROR_FILE_NOT_FOUND
+ )
+ {
+ m_hr = (HRESULT) lResult;
+ EXIT( L"Failed RegOpenKeyEx()" );
+ }
+
+
+ bSuccess = TRUE;
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::OpenLinkTrackingRegistryKey()" );
+ return( bSuccess );
+
+
+} // CMoniker::OpenLinkTrackingRegistryKey()
+
+
+BOOL CMoniker::CreateLinkTrackingRegistryKey()
+{
+
+ BOOL bSuccess = FALSE;
+ HKEY hkey = NULL;
+ DWORD dwDisposition = 0L;
+ long lResult = 0L;
+
+ m_hr = S_OK;
+
+ if( m_hkeyLinkTracking )
+ CloseLinkTrackingRegistryKey();
+
+ lResult = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
+ OLETRACKING_KEY,
+ 0L,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS,
+ NULL,
+ &m_hkeyLinkTracking,
+ &dwDisposition
+ );
+
+ if( lResult != ERROR_SUCCESS )
+ {
+ m_hr = (HRESULT) lResult;
+ EXIT( L"Failed RegCreateKeyEx()" );
+ }
+
+ bSuccess = TRUE;
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::CreateLinkTrackingRegistryKey()" );
+ return( bSuccess );
+
+} // CMoniker::CreateLinkTrackingRegistryKey()
+
+
+
+BOOL CMoniker::CloseLinkTrackingRegistryKey()
+{
+ m_hr = S_OK;
+
+ if( m_hkeyLinkTracking )
+ RegCloseKey( m_hkeyLinkTracking );
+
+ m_hkeyLinkTracking = NULL;
+
+ return TRUE;
+
+} // CMoniker::CloseLinkTrackingRegistryKey()
+
+
+
+BOOL CMoniker::SaveRegistryTrackFlags()
+{
+ BOOL bSuccess = FALSE;
+
+ long lResult = 0L;
+ DWORD dwType = 0L;
+ DWORD dwcbData = sizeof( m_dwTrackFlags );
+
+
+ m_hr = S_OK;
+
+ if( !OpenLinkTrackingRegistryKey() )
+ EXIT( L"Could not open the registry" );
+
+ lResult = RegQueryValueEx( m_hkeyLinkTracking,
+ OLETRACKING_FILEMONIKER_VALUE,
+ NULL,
+ &dwType,
+ (LPBYTE) &m_dwTrackFlags,
+ &dwcbData );
+
+ if( lResult != ERROR_SUCCESS )
+ {
+ CloseLinkTrackingRegistryKey();
+
+ if( lResult != ERROR_FILE_NOT_FOUND )
+ {
+ m_hr = (HRESULT) lResult;
+ EXIT( L"Failed RegQueryValueEx()" );
+ }
+ }
+
+
+ bSuccess = TRUE;
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::SaveRegistryTrackFlags()" );
+ return( bSuccess );
+
+} // CMoniker::SaveRegistryTrackFlags()
+
+
+
+BOOL CMoniker::DeleteRegistryTrackFlags()
+{
+ BOOL bSuccess = FALSE;
+
+ long lResult = 0L;
+ DWORD dwType = 0L;
+ DWORD dwcbData = sizeof( m_dwTrackFlags );
+
+
+ m_hr = S_OK;
+
+ if( !CreateLinkTrackingRegistryKey() )
+ EXIT( L"Could not open the registry" );
+
+
+ lResult = RegDeleteValue( m_hkeyLinkTracking,
+ OLETRACKING_FILEMONIKER_VALUE );
+
+
+ if( lResult != ERROR_SUCCESS
+ &&
+ lResult != ERROR_FILE_NOT_FOUND
+ )
+ {
+ if( lResult != ERROR_FILE_NOT_FOUND )
+ {
+ m_hr = (HRESULT) lResult;
+ EXIT( L"Failed RegDeleteValue()" );
+ }
+ }
+
+ bSuccess = TRUE;
+
+Exit:
+
+ CloseLinkTrackingRegistryKey();
+
+ DisplayErrors( bSuccess, L"CMoniker::DeleteRegistryTrackFlags()" );
+ return( bSuccess );
+
+} // CMoniker::DeleteRegistryTrackFlags()
+
+
+
+BOOL CMoniker::RestoreRegistryTrackFlags()
+{
+ BOOL bSuccess = FALSE;
+ long lResult = 0L;
+
+
+ m_hr = S_OK;
+
+ // If the registry key doesn't exist, then there's no flags
+ // to restore.
+
+ if( m_hkeyLinkTracking )
+ {
+
+ lResult = RegSetValueEx( m_hkeyLinkTracking,
+ OLETRACKING_FILEMONIKER_VALUE,
+ 0L,
+ REG_DWORD,
+ (LPBYTE) &m_dwTrackFlags,
+ sizeof( m_dwTrackFlags )
+ );
+
+ if( lResult != ERROR_SUCCESS )
+ {
+ m_hr = (HRESULT) lResult;
+ EXIT( L"Failed RegSetValueEx()" );
+ }
+
+ CloseLinkTrackingRegistryKey();
+
+ }
+
+
+ bSuccess = TRUE;
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::RestoreRegistryTrackFlags()" );
+ return( bSuccess );
+
+} // CMoniker::RestoreRegistryTrackFlags()
+
+CMoniker::SetTrackFlagsInRegistry( DWORD dwTrackFlags )
+{
+ BOOL bSuccess = FALSE;
+ long lResult = 0L;
+ HKEY hkey = NULL;
+
+
+ m_hr = S_OK;
+
+ if( !CreateLinkTrackingRegistryKey() )
+ EXIT( L"Could not create registry key" );
+
+ lResult = RegSetValueEx( m_hkeyLinkTracking,
+ OLETRACKING_FILEMONIKER_VALUE,
+ 0L,
+ REG_DWORD,
+ (LPBYTE) &dwTrackFlags,
+ sizeof( dwTrackFlags )
+ );
+
+ if( lResult != ERROR_SUCCESS )
+ {
+ m_hr = (HRESULT) lResult;
+ EXIT( L"Failed RegSetValueEx()" );
+ }
+
+
+ bSuccess = TRUE;
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::SetTrackFlagsInRegistry()" );
+ return( bSuccess );
+
+} // CMoniker::SetTrackFlagsInRegistry()
+
+
+BOOL CMoniker::CreateFileMoniker()
+{
+
+ BOOL bSuccess = FALSE;
+
+ m_hr = S_OK;
+
+ // Free any existing IMoniker.
+
+ if( m_pIMoniker )
+ {
+ m_pIMoniker->Release();
+ m_pIMoniker = NULL;
+ }
+
+ // Create a root storage.
+
+ if( !CreateTemporaryStorage() )
+ EXIT( L"Could not create a temporary storage" );
+
+ // Create a default File Moniker on that root storage.
+
+ m_hr = ::CreateFileMoniker( m_wszTemporaryStorage, &m_pIMoniker );
+ EXIT_ON_FAILED( L"Failed CreateFileMoniker" );
+
+
+ bSuccess = TRUE;
+
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CMoniker::CreateFileMoniker" );
+ return( bSuccess );
+
+} // CMoniker::CreateFileMoniker()
+*/
+#endif // _FUTURE_
diff --git a/private/oleutest/cfmex/cmoniker.hxx b/private/oleutest/cfmex/cmoniker.hxx
new file mode 100644
index 000000000..7d9e1ada0
--- /dev/null
+++ b/private/oleutest/cfmex/cmoniker.hxx
@@ -0,0 +1,178 @@
+
+
+//+================================================================
+//
+// File: CMoniker.hxx
+//
+// Purpose: This file declares the CMoniker class.
+// This class manages a file moniker.
+//
+//+================================================================
+
+
+#ifndef _C_MONIKER_HXX_
+#define _C_MONIKER_HXX_
+
+
+// --------
+// Includes
+// --------
+
+#include "CDir.hxx"
+
+// --------
+// CMoniker
+// --------
+
+class CMoniker
+{
+
+// (De)Construction
+
+public:
+
+ CMoniker();
+ ~CMoniker();
+
+// Public member routines.
+
+public:
+
+ BOOL GetTemporaryStorageTime( FILETIME *);
+ BOOL Initialize( const CDirectory& cDirectoryOriginal,
+ const CDirectory& cDirectoryFinal );
+ BOOL CreateFileMonikerEx( DWORD dwTrackFlags = 0L );
+ BOOL SaveDeleteLoad();
+ BOOL ComposeWith();
+ BOOL Reduce( DWORD dwDelay, IMoniker** ppmkReduced = NULL );
+ BOOL GetDisplayName( WCHAR * wszDisplayName, IMoniker* pmnkCaller = NULL );
+ BOOL GetTimeOfLastChange( FILETIME *ft );
+ BOOL BindToStorage();
+ BOOL BindToObject();
+
+ BOOL CreateTemporaryStorage();
+ BOOL RenameTemporaryStorage();
+ BOOL DeleteTemporaryStorage();
+
+ const WCHAR * GetTemporaryStorageName() const;
+ IBindCtx* GetBindCtx() const;
+ BOOL TouchTemporaryStorage();
+ HRESULT GetHResult() const;
+ void SuppressErrorMessages( BOOL bSuppress );
+ BOOL InitializeBindContext( );
+
+
+// Private member routines.
+
+private:
+
+ BOOL CreateLinkTrackingRegistryKey();
+ BOOL OpenLinkTrackingRegistryKey();
+ BOOL CloseLinkTrackingRegistryKey();
+ void DisplayErrors( BOOL bSuccess, WCHAR * wszFunctionName ) const;
+
+
+// Private data members
+
+private:
+
+ WCHAR m_wszSystemTempPath[ MAX_PATH + sizeof( L'\0' ) ];
+ WCHAR m_wszTemporaryStorage[ MAX_PATH + sizeof( L'\0' ) ];
+
+ IMoniker* m_pIMoniker;
+ IBindCtx* m_pIBindCtx;
+ IStorage* m_pIStorage;
+
+ WCHAR m_wszErrorMessage[ 100 ];
+ DWORD m_dwTrackFlags;
+ BOOL m_bSuppressErrorMessages;
+
+ const CDirectory* m_pcDirectoryOriginal;
+ const CDirectory* m_pcDirectoryFinal;
+
+ // The following key, along with being a usable handle, is a flag
+ // which indicates if we need to restore the data in the registry.
+
+ HKEY m_hkeyLinkTracking;
+
+
+ // Note that m_hr is used for more than just HRESULTs, sometimes
+ // it is used for other errors as well.
+
+ HRESULT m_hr;
+
+};
+
+
+// --------------
+// Inline Members
+// --------------
+
+#define OUTFILE stdout
+
+// CMoniker::DisplayErrors
+
+inline void CMoniker::DisplayErrors( BOOL bSuccess, WCHAR * wszFunctionName ) const
+{
+ if( !bSuccess
+ &&
+ !m_bSuppressErrorMessages
+ )
+ {
+ fwprintf( OUTFILE, L"Error in %s (%08x)\n %s\n",
+ wszFunctionName, m_hr, m_wszErrorMessage );
+ }
+}
+
+// CMoniker::GetTemporaryStorage
+
+inline const WCHAR * CMoniker::GetTemporaryStorageName() const
+{
+ return m_wszTemporaryStorage;
+}
+
+// CMoniker::GetBindCtx
+
+inline IBindCtx* CMoniker::GetBindCtx() const
+{
+ return( m_pIBindCtx );
+}
+
+// CMoniker::GetHResult
+
+inline HRESULT CMoniker::GetHResult() const
+{
+ return( m_hr );
+}
+
+// CMoniker::SuppressErrorMessages
+
+inline void CMoniker::SuppressErrorMessages( BOOL bSuppress )
+{
+ // Normalize to TRUE or FALSE
+
+ m_bSuppressErrorMessages = bSuppress ? TRUE : FALSE;
+}
+
+
+// ------
+// Macros
+// ------
+
+#define DEFAULT_TRACK_FLAGS ( TRACK_LOCALONLY )
+
+#define EXIT_ON_FAILED( error ) if( FAILED( m_hr )) \
+ {\
+ wcscpy( m_wszErrorMessage, ##error );\
+ goto Exit;\
+ }
+
+#undef EXIT
+#define EXIT( error ) \
+ {\
+ wcscpy( m_wszErrorMessage, ##error );\
+ goto Exit;\
+ }
+
+
+#endif // _C_MONIKER_HXX_
diff --git a/private/oleutest/cfmex/ctest.cxx b/private/oleutest/cfmex/ctest.cxx
new file mode 100644
index 000000000..e2de08c3c
--- /dev/null
+++ b/private/oleutest/cfmex/ctest.cxx
@@ -0,0 +1,1283 @@
+
+
+//+=======================================================================
+//
+// File: CTest.cxx
+//
+// Purpose: Define the CTest class.
+//
+// This class is the test engine for the CreateFileMonikerEx
+// (CFMEx) DRTs. All interactions with monikers are handled
+// through the CMoniker class.
+//
+//+=======================================================================
+
+
+// -------------
+// Include Files
+// -------------
+
+#define _DCOM_ // Allow DCOM extensions (e.g., CoInitializeEx).
+
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <wtypes.h>
+#include <oaidl.h>
+#include <dsys.h>
+#include <olecairo.h>
+#include "CDir.hxx"
+#include "CMoniker.hxx"
+#include "CTest.hxx"
+
+
+//+------------------------------------------------------------------------
+//
+// Function: CTest::CTest
+//
+// Synopsis: Inititialize member variables.
+//
+// Inputs: None.
+//
+// Outputs: N/A
+//
+// Effects: All members are initialized/defaulted.
+//
+//+------------------------------------------------------------------------
+
+
+CTest::CTest( )
+{
+
+ m_lError = 0L;
+ *m_wszErrorMessage = L'\0';
+
+ m_pcDirectoryOriginal = NULL;
+ m_pcDirectoryFinal = NULL;
+
+} // CTest::CTest
+
+//+------------------------------------------------------------------------------
+//
+// Function: CTest::~CTest
+//
+// Synopsis: No action.
+//
+// Inputs: N/A
+//
+// Outputs: N/A
+//
+// Effects: None.
+//
+//+------------------------------------------------------------------------------
+
+
+CTest::~CTest()
+{
+} // CTest::~CTest
+
+
+//+-------------------------------------------------------------------------------
+//
+// Function: CTest::Initialize
+//
+// Synopsis: Inititialize a CTest object.
+//
+// Inputs: CDirectory objects representing the original and final
+// locations of a link source file. The original location
+// is used in CreateTemporaryStorage(), and the final location
+// is used in RenameTemporaryStorage().
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: m_cDirectoryOriginal and m_cDirectoryFinal are set.
+//
+//+-------------------------------------------------------------------------------
+
+
+BOOL CTest::Initialize( const CDirectory& cDirectoryOriginal,
+ const CDirectory& cDirectoryFinal
+ )
+{
+ // -----
+ // Begin
+ // -----
+
+ m_pcDirectoryOriginal = &cDirectoryOriginal;
+ m_pcDirectoryFinal = &cDirectoryFinal;
+
+ m_cMoniker.Initialize( cDirectoryOriginal, cDirectoryFinal );
+
+ // ----
+ // Exit
+ // ----
+
+ return TRUE;
+
+} // CTest::Initialize
+
+//+-------------------------------------------------------------------------
+//
+// Function: CTest::CreateFileMonikerEx
+//
+// Synopsis: Verify that CreateFileMonikerEx actually creates a
+// *tracking* file moniker. This is done by creating
+// the file moniker, renaming the link source,
+// Reducing the moniker, and getting the display name
+// of the reduced moniker. Note that this tests
+// both CreateFileMonikerEx and Reduce.
+//
+// Inputs: None.
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+-------------------------------------------------------------------------
+
+
+BOOL CTest::CreateFileMonikerEx( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ WCHAR wszDisplayName[ MAX_PATH + sizeof( L'\0' ) ];
+ IMoniker* pmnkReduced = NULL;
+
+ // -----
+ // Begin
+ // -----
+
+ wprintf( L"CreateFileMonikerEx()\n" );
+ wprintf( L" Create a tracking file moniker (using CreateFileMonikerEx),\n"
+ L" move the represented file, Reduce the moniker, and get\n"
+ L" the display name from the reduced moniker. It should be the\n"
+ L" new file name. This test covers both CreateFileMonikerEx and\n"
+ L" Reduce.\n" );
+
+
+ // Create the tracking file moniker.
+
+ if( !m_cMoniker.CreateFileMonikerEx( ) )
+ EXIT( L"Could not CreateFileMonikerEx" );
+
+ // Rename the link source file.
+
+ if( !m_cMoniker.RenameTemporaryStorage() )
+ EXIT( L"Could not rename the temporary storage file" );
+
+ // Reduce the tracking file moniker
+
+ if( !m_cMoniker.Reduce( INFINITE, &pmnkReduced ))
+ EXIT( L"Could not reduce the moniker" );
+
+ // Use the reduced (non-tracking) file moniker to get the display name.
+
+ if( !m_cMoniker.GetDisplayName( wszDisplayName, pmnkReduced ))
+ EXIT( L"Could not get the display name" );
+
+ // Validate the display name.
+
+ if( _wcsicmp( wszDisplayName, m_cMoniker.GetTemporaryStorageName() ))
+ EXIT( L"Failed" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ m_cMoniker.DeleteTemporaryStorage();
+
+ DisplayErrors( bSuccess, L"CTest::CreateFileMonikerEx()" );
+ return( bSuccess );
+
+} // CTest::CreateFileMonikerEx()
+
+
+//+--------------------------------------------------------------------------
+//
+// Function: CTest::GetDisplayName
+//
+// Synopsis: Create a tracking file moniker, get its display name,
+// rename the link source file, and get the display name again.
+// The value of the second display name will depend on whether
+// or not the original and final link source are within the set
+// of local indexed volumes.
+//
+// Inputs: None.
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+--------------------------------------------------------------------------
+
+
+BOOL CTest::GetDisplayName( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ WCHAR wszFinalDisplayName[ MAX_PATH + sizeof( L'\0' ) ];
+ WCHAR wszOriginalDisplayName[ MAX_PATH + sizeof( L'\0' ) ];
+
+ // -----
+ // Begin
+ // -----
+
+ wprintf( L"GetDisplayName()\n" );
+ wprintf( L" Create a tracking file moniker, move the represented file,\n"
+ L" and perform a GetDisplayName on the moniker. If the original\n"
+ L" and final locations of the source file are within the set of local\n"
+ L" indexed drives, the display name will represent the final file name.\n"
+ L" Otherwise, it will represent the original display name.\n" );
+
+ // Create a tracking file moniker.
+
+ if( !m_cMoniker.CreateFileMonikerEx( ) )
+ EXIT( L"Could not CreateFileMonikerEx" );
+
+ // Get its display name.
+
+ if( !m_cMoniker.GetDisplayName( wszOriginalDisplayName ))
+ EXIT( L"Could not get the original display name" );
+
+ // Rename the link source.
+
+ if( !m_cMoniker.RenameTemporaryStorage() )
+ EXIT( L"Could not rename the temporary storage file" );
+
+ // Get the renamed moniker's display name.
+
+ if( !m_cMoniker.GetDisplayName( wszFinalDisplayName ))
+ EXIT( L"Could not get the final display name" );
+
+ // Was and is the link source on a local, indexed volume?
+
+ if( ( m_pcDirectoryOriginal->GetFileSystemType() == fstOFS )
+ &&
+ ( m_pcDirectoryOriginal->GetFileSystemType() == fstOFS )
+ )
+ {
+ // Yes, so the GetDisplayName should have tracked the rename.
+
+ if( _wcsicmp( wszFinalDisplayName, m_cMoniker.GetTemporaryStorageName() ))
+ EXIT( L"Failed" );
+ }
+ else
+ {
+ // No, so the GetDisplayName should have returned the original name.
+
+ if( _wcsicmp( wszOriginalDisplayName, wszFinalDisplayName ))
+ EXIT( L"Failed" );
+ }
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ m_cMoniker.DeleteTemporaryStorage();
+
+ DisplayErrors( bSuccess, L"CTest::GetDisplayName()" );
+ return( bSuccess );
+
+} // CTest::GetDisplayName()
+
+
+
+//+--------------------------------------------------------------------------
+//
+// Function: CTest::GetTimeOfLastChange
+//
+// Synopsis: Create a tracking file moniker, rename it, sleep, and
+// touch it. If the original and final link sources are
+// on local, indexed volumes, then a GetDisplayName should
+// return the final link source's time. Otherwise, it should
+// return the original link source's time.
+//
+// Inputs: None.
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+--------------------------------------------------------------------------
+
+
+BOOL CTest::GetTimeOfLastChange( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ FILETIME ftOriginal;
+ FILETIME ftFinal;
+ FILETIME ftMoniker;
+
+ // -----
+ // Begin
+ // -----
+
+ wprintf( L"GetTimeOfLastChange()\n" );
+ wprintf( L" Create a tracking file moniker and move it. Then touch (set the Access\n"
+ L" time) on the link source, and perform a GetTimeOfLastChange on the Moniker.\n"
+ L" If the original and final location of the link source is within the set\n"
+ L" of local indexed volumes, then the time returned from the moniker should\n"
+ L" match that of the post-touch link source. Otherwise it should match the\n"
+ L" link sources original time.\n" );
+
+ // Create a tracking file moniker.
+
+ if( !m_cMoniker.CreateFileMonikerEx( TRACK_LOCALONLY ) )
+ EXIT( L"Could not CreateFileMonikerEx" );
+
+ // Get the link source's current time.
+
+ if( !m_cMoniker.GetTemporaryStorageTime( &ftOriginal ))
+ EXIT( L"Could not get original file time" );
+
+ // Move the link source.
+
+ if( !m_cMoniker.RenameTemporaryStorage() )
+ EXIT( L"Could not rename the temporary storage file" );
+
+ // Delay so that when we touch the final link source, its time
+ // will be noticably different from the original link source's time.
+
+ {
+ wprintf( L" Sleeping to let the time change: " );
+ for( int i = 0; i < 10; i++ ) // Sleep for 5 seconds
+ {
+ wprintf( L"z" );
+ Sleep( 500 ); // 1/2 second
+ }
+ wprintf( L"\n" );
+ }
+
+ // Touch the final link source.
+
+ if( !m_cMoniker.TouchTemporaryStorage())
+ EXIT( L"Could not touch temporary storage" );
+
+ // Get the final link source's time from the file system.
+
+ if( !m_cMoniker.GetTemporaryStorageTime( &ftFinal ))
+ EXIT( L"could not get final file time" );
+
+ // Get the final link source's time from the moniker.
+
+ if( !m_cMoniker.GetTimeOfLastChange( &ftMoniker ))
+ EXIT( L"Could not get the time of last change" );
+
+ // Is the original & final location of the link source file
+ // in the set of local, indexed volumes?
+
+ if( ( m_pcDirectoryOriginal->GetFileSystemType() == fstOFS )
+ &&
+ ( m_pcDirectoryOriginal->GetFileSystemType() == fstOFS )
+ )
+ {
+ // Yes. GetTimeOfLastChange should therefore have found
+ // the time of the final link source file.
+
+ if( memcmp( &ftFinal, &ftMoniker, sizeof( FILETIME ) ))
+ EXIT( L"Failed" );
+ }
+ else
+ {
+ // No. GetTimeOfLastChange should therefore have returned
+ // the time of the original link source file.
+
+ if( memcmp( &ftOriginal, &ftMoniker, sizeof( FILETIME ) ))
+ EXIT( L"Failed" );
+ }
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ m_cMoniker.DeleteTemporaryStorage();
+
+ DisplayErrors( bSuccess, L"CTest::GetTimeOfLastChange()" );
+ return( bSuccess );
+
+} // CTest::GetTimeOfLastChange()
+
+
+//+--------------------------------------------------------------------
+//
+// Function: CTest::BindToObject
+//
+// Synopsis: Create a tracking file moniker, move the link source,
+// and attempt to bind to it with a BindToObject.
+// Since presumably the temporary file we're using as a link
+// source has no server, this will fail. But any failure
+// downstream of locating the link source file will be
+// ignored.
+//
+// Inputs: None.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+--------------------------------------------------------------------
+
+
+BOOL CTest::BindToObject( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ // -----
+ // Begin
+ // -----
+
+ wprintf( L"BindToObject()\n" );
+ wprintf( L" Create a tracking file moniker, moved the represented file,\n"
+ L" and attempt a BindToStorage() (which should succeed).\n" );
+
+ // Create a tracking file moniker.
+
+ if( !m_cMoniker.CreateFileMonikerEx( ) )
+ EXIT( L"Could not CreateFileMonikerEx" );
+
+ // Move the link source file.
+
+ if( !m_cMoniker.RenameTemporaryStorage() )
+ EXIT( L"Could not rename the temporary storage file" );
+
+ // Attempt a bind.
+
+ if( !m_cMoniker.BindToObject( ))
+ EXIT( L"Could not bind to Object" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+Exit:
+
+ m_cMoniker.DeleteTemporaryStorage();
+
+ DisplayErrors( bSuccess, L"CTest::BindToObject()" );
+ return( bSuccess );
+
+} // CTest::BindToObject()
+
+
+//+-----------------------------------------------------------
+//
+// Function: CTest::IPersist
+//
+// Synopsis: Create a tracking file moniker, move the link
+// source, and perform the CMoniker::SaveDeleteLoad
+// test.
+//
+// Inputs: None.
+//
+// Output: TRUE if successful, FALSE otherwise
+//
+// Effects: None.
+//
+//+-----------------------------------------------------------
+
+
+BOOL CTest::IPersist( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ // -----
+ // Begin
+ // -----
+
+ wprintf( L"IPersist()\n" );
+ wprintf( L" Create a tracking file moniker (using CreateFileMonikerEx), and\n"
+ L" save it to a Stream. Delete the moniker, create a new one using\n"
+ L" CreateFileMoniker, load it from the Stream, and verify that the resulting\n"
+ L" file moniker is tracking.\n" );
+
+ // Create a tracking file moniker.
+
+ if( !m_cMoniker.CreateFileMonikerEx( ) )
+ EXIT( L"Could not CreateFileMonikerEx" );
+
+ // Rename the link source.
+
+ if( !m_cMoniker.RenameTemporaryStorage() )
+ EXIT( L"Could not rename the temporary storage file" );
+
+ // Save the moniker, deleted, create a new moniker, and reload it.
+
+ if( !m_cMoniker.SaveDeleteLoad( ))
+ EXIT( L"Failed SaveDeleteLoad" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ m_cMoniker.DeleteTemporaryStorage();
+
+ DisplayErrors( bSuccess, L"CTest::IPersist()" );
+ return( bSuccess );
+
+} // CTest::IPersist()
+
+
+//+----------------------------------------------------------------------
+//
+// Function: CTest::ComposeWith
+//
+// Synopsis: Create a tracking file moniker, and use it in the
+// CMoniker::ComposeWith operation.
+//
+// Inputs: None.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+----------------------------------------------------------------------
+
+BOOL CTest::ComposeWith( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ IMoniker* pmnkReduced = NULL;
+ WCHAR wszDisplayName[ MAX_PATH + sizeof( L'\0' )];
+
+ // -----
+ // Begin
+ // -----
+
+ m_lError = 0;
+
+ wprintf( L"ComposeWith()\n" );
+ wprintf( L" Create a tracking file moniker, and compose it with a non-tracking\n"
+ L" file moniker on the right. The resulting moniker should be tracking.\n" );
+
+ // Create the tracking file moniker.
+
+ if( !m_cMoniker.CreateFileMonikerEx( ) )
+ EXIT( L"Could not CreateFileMonikerEx" );
+
+ // Perform the ComposeWith operation.
+
+ if( !m_cMoniker.ComposeWith( ))
+ EXIT( L"Failed ComposeWith" );
+
+ // Move the link source file.
+
+ if( !m_cMoniker.RenameTemporaryStorage() )
+ EXIT( L"Could not rename the temporary storage file" );
+
+ // Reduce the composed moniker.
+
+ if( !m_cMoniker.Reduce( INFINITE, &pmnkReduced ))
+ EXIT( L"Could not reduce the moniker" );
+
+ // Get the display name on the reduced moniker.
+
+ if( !m_cMoniker.GetDisplayName( wszDisplayName, pmnkReduced ))
+ EXIT( L"Could not get the display name" );
+
+ // Verify that the name from the moniker is the actual link source
+ // file's new name.
+
+ if( _wcsicmp( wszDisplayName, m_cMoniker.GetTemporaryStorageName() ))
+ EXIT( L"Failed" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ m_cMoniker.DeleteTemporaryStorage();
+
+ DisplayErrors( bSuccess, L"CTest::ComposeWith()" );
+ return( bSuccess );
+
+} // CTest::ComposeWith()
+
+
+//+------------------------------------------------------------------
+//
+// Function: CTest::BindToStorage
+//
+// Synopsis: Create a tracking file moniker, and perform the
+// CMoniker::BindToStorage operation.
+//
+// Inputs: None.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+//+------------------------------------------------------------------
+
+
+BOOL CTest::BindToStorage( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ // -----
+ // Begin
+ // -----
+
+ m_lError = 0;
+
+ wprintf( L"BindToStorage()\n" );
+ wprintf( L" Create a tracking file moniker (using CreateFileMonikerEx),\n"
+ L" move the represented file, Reduce the moniker, and get\n"
+ L" the display name from the reduced moniker. It should be the\n"
+ L" new file name. This test covers both CreateFileMonikerEx and\n"
+ L" Reduce.\n" );
+
+ // Create the tracking file moniker.
+
+ if( !m_cMoniker.CreateFileMonikerEx( ) )
+ EXIT( L"Could not CreateFileMonikerEx" );
+
+ // Move the link source file.
+
+ if( !m_cMoniker.RenameTemporaryStorage() )
+ EXIT( L"Could not rename the temporary storage file" );
+
+ // Bind to the Storage
+
+ if( !m_cMoniker.BindToStorage( ))
+ EXIT( L"Could not bind to Storage" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ m_cMoniker.DeleteTemporaryStorage();
+
+ DisplayErrors( bSuccess, L"CTest::BindToStorage()" );
+ return( bSuccess );
+
+} // CTest::BindToStorage()
+
+
+//+--------------------------------------------------------------------------
+//
+// Function: CTest::DeleteLinkSource
+//
+// Synopsis: Create a tracking file moniker, delete the link source,
+// and attempt a Reduce. The Reduce should not fail, but
+// it should return an HResult of MK_S_REDUCED_TO_SELF.
+//
+// Inputs: Tick count limit on the length of the Reduce operation.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+--------------------------------------------------------------------------
+
+BOOL CTest::DeleteLinkSource( DWORD dwDelay )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ WCHAR wszTimeout[ 80 ];
+
+ // -----
+ // Begin
+ // -----
+
+ m_lError = 0L;
+
+ // Generate a string which shows what the delay is.
+
+ switch( dwDelay )
+ {
+ case 0:
+ wcscpy( wszTimeout, L"instant timeout" );
+ break;
+
+ case INFINITE:
+ wcscpy( wszTimeout, L"infinite timeout" );
+ break;
+
+ default:
+ wsprintf( wszTimeout, L"%d ms timeout", dwDelay );
+ }
+
+ // Display a header.
+
+ wprintf( L"Delete Link Source (%s)\n",
+ wszTimeout );
+ wprintf( L" Create a tracking file moniker, then delete the link source, and\n"
+ L" attempt a reduce with a %s.\n",
+ wszTimeout );
+
+ // Create a tracking file moniker.
+
+ if( !m_cMoniker.CreateFileMonikerEx( ) )
+ EXIT( L"Could not CreateFileMonikerEx" );
+
+ // Delete the link source file.
+
+ if( !m_cMoniker.DeleteTemporaryStorage() )
+ EXIT( L"Could not delete temporary storage" );
+
+ // Tell CMoniker not to alarm the user with error messages.
+
+ m_cMoniker.SuppressErrorMessages( TRUE );
+
+ // Reduce the moniker, and verify it returns the proper Success code.
+
+ if( !m_cMoniker.Reduce( dwDelay )
+ ||
+ m_cMoniker.GetHResult() != MK_S_REDUCED_TO_SELF
+ )
+ EXIT( L"Failed" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ m_cMoniker.DeleteTemporaryStorage();
+ m_cMoniker.SuppressErrorMessages( FALSE );
+
+ DisplayErrors( bSuccess, L"CTest::DeleteLinkSource()" );
+ return( bSuccess );
+
+} // CTest::DeleteLinkSource()
+
+
+//+------------------------------------------------------------------
+//
+// Function: CTest::GetOversizedBindOpts
+//
+// Synopsis: Test a bind context's ability to return a BIND_OPTS
+// which is larger than expected.
+//
+// First, initialize the default BIND_OPTS in the bind
+// context to 1s. Then, ask for a large BIND_OPTS.
+// The resulting buffer should have 1s up to the size of
+// the normal BIND_OPTS, and 0s for the remainder of the
+// buffer. The length, however, should be that of the large
+// buffer.
+//
+// Inputs: None.
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+------------------------------------------------------------------
+
+
+BOOL CTest::GetOversizedBindOpts( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ BIND_OPTS bind_opts;
+ LPBIND_OPTS pbind_optsLarge = (LPBIND_OPTS) new BYTE[ SIZE_OF_LARGE_BIND_OPTS ];
+ LPBIND_OPTS pbind_optsExpected = (LPBIND_OPTS) new BYTE[ SIZE_OF_LARGE_BIND_OPTS ];
+
+ // -----
+ // Begin
+ // -----
+
+ m_lError = 0L;
+
+ wprintf( L"Get Oversized BindOpts\n" );
+ wprintf( L" Create a buffer which is much larger than a normal BIND_OPTS, and\n"
+ L" use it to request the BIND_OPTS from a bind context. The length\n"
+ L" of the resulting buffer should be the large value, and the extra\n"
+ L" room in the buffer should be all 0s.\n" );
+
+
+ // Validate our 'new' operations.
+
+ if( pbind_optsLarge == NULL )
+ EXIT( L"Could not allocate pbind_optsLarge" );
+ pbind_optsLarge->cbStruct = SIZE_OF_LARGE_BIND_OPTS;
+
+ if( pbind_optsExpected == NULL )
+ EXIT( L"Could not allocate pbind_optsExpected" );
+ pbind_optsExpected->cbStruct = SIZE_OF_LARGE_BIND_OPTS;
+
+
+ // Initialize the bind_opts (normal sized) in the bind context to 1s.
+
+ if( !m_cMoniker.InitializeBindContext() )
+ EXIT( L"Could not initialize the bind context" );
+
+ memset( &bind_opts, 1, sizeof( bind_opts ));
+ bind_opts.cbStruct = sizeof( bind_opts );;
+
+ m_lError = m_cMoniker.GetBindCtx()->SetBindOptions( &bind_opts );
+ if( FAILED( m_lError )) EXIT( L"Could not set original bind options" );
+
+ // Initialize the large bind_opts to 2s, then retrieve the bind_opts into
+ // this structure. This is done to verify that the GetBindOptions below
+ // fills in the entire requested buffer (overwirting all of the 2s).
+
+ memset( pbind_optsLarge, 2, SIZE_OF_LARGE_BIND_OPTS );
+ pbind_optsLarge->cbStruct = SIZE_OF_LARGE_BIND_OPTS;
+
+ m_lError = m_cMoniker.GetBindCtx()->GetBindOptions( pbind_optsLarge );
+ if( FAILED( m_lError )) EXIT( L"Could not get large bind options" );
+
+ // The returned structure should have the large cbStruct, 1s up to
+ // the length of BIND_OPTS, and 0s after that.
+
+ memset( pbind_optsExpected, 0, SIZE_OF_LARGE_BIND_OPTS );
+ memset( pbind_optsExpected, 1, sizeof( bind_opts ));
+ pbind_optsExpected->cbStruct = SIZE_OF_LARGE_BIND_OPTS;
+
+ if( memcmp( pbind_optsLarge, pbind_optsExpected, SIZE_OF_LARGE_BIND_OPTS ))
+ EXIT( L"Failed" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CTest::GetOversizedBindOpts()" );
+ return( bSuccess );
+
+} // CTest::GetOversizeBindOpts()
+
+
+
+//+----------------------------------------------------------------
+//
+// Function: CTest::GetUndersizedBindOpts
+//
+// Synopsis: Create a bind context, and initialize the data in its
+// BIND_OPTS to 1s. Create a normal BIND_OPTS-sized buffer,
+// initialize it to 2s, then use it to get a small-sized
+// BIND_OPTS from the bind context. The resulting buffer
+// should have a small length, a small number of 1s,
+// and 2s for the remainder of the buffer.
+//
+// Inputs: None.
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+----------------------------------------------------------------
+
+
+
+BOOL CTest::GetUndersizedBindOpts( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ LPBIND_OPTS pbind_optsSmall = (LPBIND_OPTS) new BYTE[ SIZE_OF_LARGE_BIND_OPTS ];
+ LPBIND_OPTS pbind_optsExpected = (LPBIND_OPTS) new BYTE[ SIZE_OF_LARGE_BIND_OPTS ];
+ BIND_OPTS bind_opts;
+ bind_opts.cbStruct = sizeof( bind_opts );
+
+ // -----
+ // Begin
+ // -----
+
+ m_lError = 0L;
+
+ wprintf( L"Get Undersized BindOpts\n" );
+ wprintf( L" Get the BIND_OPTS from a bind context, but only providing a small\n"
+ L" buffer, and verify that only that portion of the actual BIND_OPTS\n"
+ L" is returned.\n" );
+
+ // Validate our 'new's.
+
+ if( pbind_optsSmall == NULL )
+ EXIT( L"Could not allocate pbind_optsSmall" );
+ pbind_optsSmall->cbStruct = SIZE_OF_SMALL_BIND_OPTS;
+
+ if( pbind_optsExpected == NULL )
+ EXIT( L"Could not allocate pbind_optsExpected" );
+ pbind_optsExpected->cbStruct = SIZE_OF_SMALL_BIND_OPTS;
+
+
+ // Initialize the bind_opts (normal sized) in the bind context to 1s.
+
+ if( !m_cMoniker.InitializeBindContext() )
+ EXIT( L"Could not initialize the bind context" );
+
+ memset( &bind_opts, 1, sizeof( bind_opts ));
+ bind_opts.cbStruct = sizeof( bind_opts );;
+
+ m_lError = m_cMoniker.GetBindCtx()->SetBindOptions( &bind_opts );
+ if( FAILED( m_lError )) EXIT( L"Could not set original bind options" );
+
+
+ // Initialize the small bind_opts to 2s, then retrieve the bind_opts into
+ // this structure.
+
+ memset( pbind_optsSmall, 2, SIZE_OF_LARGE_BIND_OPTS );
+ pbind_optsSmall->cbStruct = SIZE_OF_SMALL_BIND_OPTS;
+
+ m_lError = m_cMoniker.GetBindCtx()->GetBindOptions( pbind_optsSmall );
+ if( FAILED( m_lError )) EXIT( L"Could not get small bind options" );
+
+ // The returned structure should have the small cbStruct, 1s up to
+ // the end of the small buffer, and 2s to the end of the real buffer.
+
+ memset( pbind_optsExpected, 2, SIZE_OF_LARGE_BIND_OPTS );
+ memset( pbind_optsExpected, 1, SIZE_OF_SMALL_BIND_OPTS );
+ pbind_optsExpected->cbStruct = SIZE_OF_SMALL_BIND_OPTS;
+
+ if( memcmp( pbind_optsSmall, pbind_optsExpected, SIZE_OF_LARGE_BIND_OPTS ))
+ EXIT( L"Failed" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CTest::GetUndersizedBindOpts()" );
+ return( bSuccess );
+
+} // CTest::GetUndersizedBindOpts()
+
+
+//+----------------------------------------------------------------
+//
+// Function: CTest::SetOversizedBindOpts
+//
+// Synopsis: Create a large BIND_OPTS buffer, set it in
+// a bind context, and verify that it can be read back.
+//
+// Inputs: None.
+//
+// Outputs: TRUE if successful, FALSE otherwise.
+//
+// Effects: None.
+//
+//+----------------------------------------------------------------
+
+BOOL CTest::SetOversizedBindOpts( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ LPBIND_OPTS pbind_optsLarge = (LPBIND_OPTS) new BYTE[ SIZE_OF_LARGE_BIND_OPTS ];
+ LPBIND_OPTS pbind_optsExpected = (LPBIND_OPTS) new BYTE[ SIZE_OF_LARGE_BIND_OPTS ];
+
+ // -----
+ // Begin
+ // -----
+
+ wprintf( L"Set Oversized BindOpts\n" );
+ wprintf( L" Set a larger-than-usual BIND_OPTS in a bind context, and verify\n"
+ L" that it can be read back in its entirety.\n" );
+
+ // Validate our 'new's.
+
+ if( pbind_optsLarge == NULL )
+ EXIT( L"Could not allocate pbind_optsLarge" );
+ pbind_optsLarge->cbStruct = SIZE_OF_LARGE_BIND_OPTS;
+
+ if( pbind_optsExpected == NULL )
+ EXIT( L"Could not allocate pbind_optsExpected" );
+ pbind_optsExpected->cbStruct = SIZE_OF_LARGE_BIND_OPTS;
+
+ // Initialize the large bind_opts to 1s, then set the large BIND_OPTS
+ // into the bind context.
+
+ if( !m_cMoniker.InitializeBindContext() )
+ EXIT( L"Could not initialize the bind context" );
+
+ memset( pbind_optsLarge, 1, SIZE_OF_LARGE_BIND_OPTS );
+ pbind_optsLarge->cbStruct = SIZE_OF_LARGE_BIND_OPTS;
+
+ m_lError = m_cMoniker.GetBindCtx()->SetBindOptions( pbind_optsLarge );
+ if( FAILED( m_lError )) EXIT( L"Could not set large bind options" );
+
+ // Get the BIND_OPTS back from the bind context, and verify that it's
+ // what we set.
+
+ memset( pbind_optsLarge, 0, SIZE_OF_LARGE_BIND_OPTS );
+ pbind_optsLarge->cbStruct = SIZE_OF_LARGE_BIND_OPTS;
+ m_lError = m_cMoniker.GetBindCtx()->GetBindOptions( pbind_optsLarge );
+
+ memset( pbind_optsExpected, 1, SIZE_OF_LARGE_BIND_OPTS );
+ pbind_optsExpected->cbStruct = SIZE_OF_LARGE_BIND_OPTS;
+
+ if( memcmp( pbind_optsLarge, pbind_optsExpected, SIZE_OF_LARGE_BIND_OPTS ))
+ EXIT( L"Failed" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CTest::SetOversizedBindOpts()" );
+ return( bSuccess );
+
+} // CTest::SetOversizeBindOpts()
+
+
+//+------------------------------------------------------------
+//
+// Function: CTest::SetUndersizedBindOpts
+//
+// Synopsis: Create a bind context, and initialize its BIND_OPTS to
+// all 1s. Then, set a small BIND_OPTS in the bind context
+// which is set to 2s. Read back the whold BIND_OPTS, and
+// verify that it has the normal length, 2s at the
+// beginning, and 1s to the end.
+//
+// Inputs: None.
+//
+// Output: TRUE if successful, FALSE otherwise.
+//
+//+------------------------------------------------------------
+
+
+BOOL CTest::SetUndersizedBindOpts( )
+{
+ // ---------------
+ // Local Variables
+ // ---------------
+
+ BOOL bSuccess = FALSE;
+
+ BIND_OPTS bind_opts;
+ BIND_OPTS bind_optsExpected;
+
+ // -----
+ // Begin
+ // -----
+
+ m_lError = 0L;
+
+ wprintf( L"Set Undersized BindOpts\n" );
+ wprintf( L" Initialize a BIND_OPTS in a bind context to 1s, then set a smaller\n"
+ L" BIND_OPTS in it set to 2s. When the buffer is read back, it should\n"
+ L" have 2s up to the small buffer size, followed by 1s.\n" );
+
+ // Initialize the bind context's BIND_OPTS (normal sized) to 1s.
+
+ if( !m_cMoniker.InitializeBindContext() )
+ EXIT( L"Could not initialize the bind context" );
+
+ memset( &bind_opts, 1, sizeof( bind_opts ));
+ bind_opts.cbStruct = sizeof( bind_opts );
+ m_lError = m_cMoniker.GetBindCtx()->SetBindOptions( &bind_opts );
+ if( FAILED( m_lError )) EXIT( L"Could not set initial BIND_OPTS" );
+
+ // Now set a smaller BIND_OPTS. But initialize the local BIND_OPTS to all
+ // 2s, so that we can verify that only part of the buffer is put into the
+ // bind context.
+
+ memset( &bind_opts, 2, sizeof( bind_opts ) );
+ bind_opts.cbStruct = SIZE_OF_SMALL_BIND_OPTS;
+ m_lError = m_cMoniker.GetBindCtx()->SetBindOptions( &bind_opts );
+ if( FAILED( m_lError )) EXIT( L"Could not set small BIND_OPTS" );
+
+ // The resulting BIND_OPTS in the bind context should have 2s up
+ // to SIZE_OF_SMALL_BIND_OPTS, and 1s for the remainder of the buffer.
+
+ bind_opts.cbStruct = sizeof( bind_opts );
+ m_lError = m_cMoniker.GetBindCtx()->GetBindOptions( &bind_opts );
+ if( FAILED( m_lError )) EXIT( L"Could not get BIND_OPTS" );
+
+ memset( &bind_optsExpected, 1, sizeof( bind_opts ));
+ memset( &bind_optsExpected, 2, SIZE_OF_SMALL_BIND_OPTS );
+ bind_optsExpected.cbStruct = sizeof( bind_opts );
+
+ if( memcmp( &bind_opts, &bind_optsExpected, sizeof( bind_opts )))
+ EXIT( L"Failed" );
+
+
+ bSuccess = TRUE;
+
+ // ----
+ // Exit
+ // ----
+
+Exit:
+
+ DisplayErrors( bSuccess, L"CTest::SetUndersizedBindOpts()" );
+ return( bSuccess );
+
+} // CTest::SetUndersizedBindOpts()
+
+
+#ifdef _FUTURE_
+
+BOOL CTest::CFMWithRegistryClear( )
+{
+ BOOL bSuccess = FALSE;
+ WCHAR wszDisplayName[ MAX_PATH + sizeof( L'\0' ) ];
+
+ if( !m_cMoniker.SaveRegistryTrackFlags() )
+ EXIT( L"Could not save the existing track flags in the registry" );
+
+ if( !m_cMoniker.DeleteRegistryTrackFlags() )
+ EXIT( L"Could not delete the existing track flags in the registry" );
+
+ if( !m_cMoniker.CreateFileMoniker() )
+ EXIT( L"Could not CreateFileMoniker" );
+
+ if( !m_cMoniker.RenameTemporaryStorage() )
+ EXIT( L"Could not rename the temporary storage file" );
+
+ if( !m_cMoniker.GetDisplayName( wszDisplayName ))
+ EXIT( L"Could not get the display name" );
+
+ if( !_wcsicmp( wszDisplayName, m_cMoniker.GetTemporaryStorageName() ))
+ EXIT( L"Test failed: A moniker was created using CreateFileMoniker, after\n"
+ L" the Track Flags had been cleared from the Registry.\n"
+ L" The underlying root storage was then renamed.\n"
+ L" But a GetDisplayName then showed the new name, implying\n"
+ L" that the created moniker is using tracking (which it\n"
+ L" should not, because without the Track Flags set there\n"
+ L" should be no tracking).\n" );
+
+
+ bSuccess = TRUE;
+
+Exit:
+
+ m_cMoniker.DeleteTemporaryStorage();
+
+ if( !m_cMoniker.RestoreRegistryTrackFlags() )
+ ERROR_IN_EXIT( L"Could not restore Track Flags in Registry" );
+
+
+ DisplayErrors( bSuccess, L"CTest::CFMWithRegistryClear" );
+ return( bSuccess );
+
+} // CTest::CFMWithRegistryClear
+
+
+
+
+BOOL CTest::CFMWithRegistrySet( )
+{
+ BOOL bSuccess = FALSE;
+ WCHAR wszDisplayName[ MAX_PATH + sizeof( L'\0' ) ];
+
+ if( !m_cMoniker.SaveRegistryTrackFlags() )
+ EXIT( L"Could not save the existing track flags in the registry" );
+
+ if( !m_cMoniker.SetTrackFlagsInRegistry( TRACK_LOCALONLY ) )
+ EXIT( L"Could not set track flags in the registry" );
+
+ if( !m_cMoniker.CreateFileMoniker() )
+ EXIT( L"Could not CreateFileMoniker" );
+
+ if( !m_cMoniker.RenameTemporaryStorage() )
+ EXIT( L"Could not rename the temporary storage file" );
+
+ if( !m_cMoniker.GetDisplayName( wszDisplayName ))
+ EXIT( L"Could not get the display name" );
+
+ if( _wcsicmp( wszDisplayName, m_cMoniker.GetTemporaryStorageName() ))
+ EXIT( L"Test failed: A moniker was created using CreateFileMoniker, after\n"
+ L" setting the TRACK_LOCALONLY flag in the Registry.\n"
+ L" However, after moving the linked file, GetDisplayName\n"
+ L" did not return the new name.\n" );
+
+
+ bSuccess = TRUE;
+
+Exit:
+
+ m_cMoniker.DeleteTemporaryStorage();
+
+ if( !m_cMoniker.RestoreRegistryTrackFlags() )
+ ERROR_IN_EXIT( L"Could not restore Track Flags in Registry" );
+
+
+ DisplayErrors( bSuccess, L"CTest::CFMWithRegistrySet()" );
+ return( bSuccess );
+
+} // CTest::CFMWithRegistrySet
+
+
+#endif // _FUTURE_
diff --git a/private/oleutest/cfmex/ctest.hxx b/private/oleutest/cfmex/ctest.hxx
new file mode 100644
index 000000000..32d1e0012
--- /dev/null
+++ b/private/oleutest/cfmex/ctest.hxx
@@ -0,0 +1,118 @@
+
+
+//+================================================================
+//
+// File: CTest.hxx
+//
+// Purpose: Declare the CTest class.
+//
+// This class is the test engine for the CreateFileMonikerEx
+// API (CFMEx) DRTs.
+//
+//+================================================================
+
+#ifndef _C_TEST_HXX_
+#define _C_TEST_HXX_
+
+// --------
+// Includes
+// --------
+
+#include "CDir.hxx"
+
+// -----
+// CTest
+// -----
+
+class CTest
+{
+
+// (De)Construction
+
+public:
+
+ CTest( );
+ ~CTest();
+
+// Public member routines.
+
+public:
+
+ BOOL Initialize( const CDirectory& cDirectoryOriginal,
+ const CDirectory& cDirectoryFinal );
+ BOOL CreateFileMonikerEx();
+ BOOL BindToStorage();
+ BOOL BindToObject();
+ BOOL IPersist();
+ BOOL ComposeWith();
+ BOOL GetDisplayName();
+ BOOL GetTimeOfLastChange();
+ BOOL DeleteLinkSource( DWORD dwDelay = INFINITE );
+
+ BOOL GetOversizedBindOpts();
+ BOOL GetUndersizedBindOpts();
+ BOOL SetOversizedBindOpts();
+ BOOL SetUndersizedBindOpts();
+
+// Private member routines.
+
+private:
+
+ void DisplayErrors( BOOL bSuccess, WCHAR * wszFunctionName );
+
+// Private member variables.
+
+private:
+
+ CMoniker m_cMoniker;
+ long m_lError;
+ WCHAR m_wszErrorMessage[ 512 ];
+
+ const CDirectory* m_pcDirectoryOriginal;
+ const CDirectory* m_pcDirectoryFinal;
+
+};
+
+
+// --------------
+// Inline Members
+// --------------
+
+#define OUTFILE stdout
+
+inline void CTest::DisplayErrors( BOOL bSuccess, WCHAR * wszFunctionName )
+{
+ if( !bSuccess )
+ fwprintf( OUTFILE, L"Error in %s (%08x)\n %s \n",
+ wszFunctionName, m_lError, m_wszErrorMessage );
+}
+
+
+// ------
+// Macros
+// ------
+
+#undef EXIT
+#define EXIT( error ) \
+ {\
+ wcscpy( m_wszErrorMessage, ##error );\
+ goto Exit;\
+ }
+
+
+#define ERROR_IN_EXIT( error ) \
+ {\
+ wcscpy( m_wszErrorMessage, ##error );\
+ bSuccess = FALSE; \
+ }
+
+
+// -------
+// Defines
+// -------
+
+#define SIZE_OF_LARGE_BIND_OPTS ( 2 * sizeof( BIND_OPTS2 ))
+#define SIZE_OF_SMALL_BIND_OPTS ( 3 * sizeof( DWORD ))
+
+#endif // _C_TEST_HXX_
+
diff --git a/private/oleutest/cfmex/makefile b/private/oleutest/cfmex/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/oleutest/cfmex/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/oleutest/cfmex/sources b/private/oleutest/cfmex/sources
new file mode 100644
index 000000000..0bfd19e12
--- /dev/null
+++ b/private/oleutest/cfmex/sources
@@ -0,0 +1,35 @@
+!IF 0
+
+Copyright (c) 1995 Microsoft Corporation
+
+!ENDIF
+
+MAJORCOMP= appl
+MINORCOMP= windows
+TARGETNAME= CFMEx
+TARGETPATH= obj
+TARGETTYPE= LIBRARY
+
+
+
+INCLUDES=.
+
+C_DEFINES= \
+ $(C_DEFINES)
+
+
+SOURCES= CDir.cxx CMoniker.cxx CTest.cxx
+
+UMTYPE= console
+UMAPPL= CFMEx
+
+UMLIBS= .\obj\*\CFMEx.lib \
+ $(BASEDIR)\public\sdk\lib\cairo\*\ole32.lib \
+ $(WIN32_LIBS) \
+ $(GUI32_LIBS) \
+ $(BASEDIR)\public\sdk\lib\*\uuid.lib \
+ $(BASEDIR)\public\sdk\lib\i386\oleaut32.lib \
+ $(BASEDIR)\public\sdk\lib\cairo\i386\dsysuuid.lib
+
+
+CAIRO_PRODUCT=1