/// <summary> /// MarshalPropVariant /// </summary> /// <param name="obj">Object to marshal, should be DateTime or String</param> /// <returns>newly allocated PROPVARIANT structure</returns> internal static IntPtr MarshalPropVariant(Object obj) { IntPtr pszVal = IntPtr.Zero; IntPtr pNative = IntPtr.Zero; try { PROPVARIANT v; if (obj is string) { pszVal = Marshal.StringToCoTaskMemAnsi((string)obj); v = new PROPVARIANT(); v.vt = VARTYPE.VT_LPSTR; v.union.pszVal = pszVal; } else if (obj is DateTime) { v = new PROPVARIANT(); v.vt = VARTYPE.VT_FILETIME; long longFileTime = ((DateTime)obj).ToFileTime(); v.union.filetime.dwLowDateTime = (Int32)longFileTime; v.union.filetime.dwHighDateTime = (Int32)((longFileTime >> 32) & 0xFFFFFFFF); } else { throw new InvalidOperationException( SR.Get(SRID.FilterGetValueMustBeStringOrDateTime)); } // allocate an unmanaged PROPVARIANT to return pNative = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(PROPVARIANT))); // Per MSDN, AllocCoTaskMem never returns null: check for IntPtr.Zero instead. Invariant.Assert(pNative != IntPtr.Zero); // marshal the managed PROPVARIANT into the unmanaged block and return it Marshal.StructureToPtr(v, pNative, false); } catch { if (pszVal != IntPtr.Zero) { Marshal.FreeCoTaskMem(pszVal); } if (pNative != IntPtr.Zero) { Marshal.FreeCoTaskMem(pNative); } throw; } return(pNative); }
SetOleProperty( Guid fmtid, uint propId, object propVal ) { CheckDisposed(); IPropertyStorage ps = fmtid == FormatId.SummaryInformation ? _psSummInfo : _psDocSummInfo; if (ps == null) { // // The property set does not exist, so create it. // if (propVal != null) { _pss.Create( ref fmtid, ref fmtid, SafeNativeCompoundFileConstants.PROPSETFLAG_ANSI, (uint)_grfMode, out ps ); if (fmtid == FormatId.SummaryInformation) { _psSummInfo = ps; } else { _psDocSummInfo = ps; } } else { // // But if we were going to delete the property anyway, there's // nothing to do. // return; } } PROPSPEC[] propSpecs = new PROPSPEC[1]; PROPVARIANT[] vals = new PROPVARIANT[1]; propSpecs[0].propType = (uint)PropSpecType.Id; propSpecs[0].union.propId = propId; if (propVal == null) { // // New value is null => remove the property. Unlike in the case of ReadMultiple, // we can just let this one throw an exception on failure. There are no non-zero // success codes to worry about. // ps.DeleteMultiple(1, propSpecs); return; } // // New value is non-null => set a new value for the property. // IntPtr pszVal = IntPtr.Zero; try { if (propVal is string) { // // 1) We store string properties internally as UTF-16. // During save, convert the string (UTF-16) to CP_ACP and back // 2) If property value changed during that process, store it in CF OLE Storage as UTF-8 // 3) Otherwise store it as CP_ACP // string inputString = propVal as string; pszVal = Marshal.StringToCoTaskMemAnsi(inputString); string convertedString = Marshal.PtrToStringAnsi(pszVal); if (String.CompareOrdinal(inputString, convertedString) != 0) { // The string is not an ASCII string. Use UTF-8 to encode it! byte[] byteArray = UTF8Encoding.UTF8.GetBytes(inputString); int nLen = byteArray.Length; // // Before memory allocation for holding UTF-8 codes, we need to first free the memory // allocated by Marshal.StringToCoTaskMemAnsi(). // Note that if there is any exception in this try scope, the memory will still be released // by the finally of this try scope. // if (pszVal != IntPtr.Zero) { Marshal.FreeCoTaskMem(pszVal); pszVal = IntPtr.Zero; } pszVal = Marshal.AllocCoTaskMem(checked (nLen + 1)); //The extra one byte is for the string terminator null. Marshal.Copy(byteArray, 0, pszVal, nLen); Marshal.WriteByte(pszVal, nLen, 0); //Put the string terminator null at the end of the array. } vals[0].vt = VARTYPE.VT_LPSTR; vals[0].union.pszVal = pszVal; } else if (propVal is DateTime) { // set FileTime as an Int64 to avoid pointer operations vals[0].vt = VARTYPE.VT_FILETIME; vals[0].union.hVal = ((DateTime)propVal).ToFileTime(); } else { throw new ArgumentException( SR.Get(SRID.InvalidDocumentPropertyType, propVal.GetType().ToString()), "propVal"); } // // Again, we can just let it throw on failure; no non-zero success codes. It won't throw // if the property doesn't exist. // ps.WriteMultiple(1, propSpecs, vals, 0); } finally { if (pszVal != IntPtr.Zero) { Marshal.FreeCoTaskMem(pszVal); } } }
GetOleProperty( Guid fmtid, uint propId ) { CheckDisposed(); // fmtid is always either DocSum or Sum. IPropertyStorage ps = fmtid == FormatId.SummaryInformation ? _psSummInfo : _psDocSummInfo; if (ps == null) { // This file doesn't even contain the property storage that this // property belongs to, so it certainly doesn't contain the property. return(null); } object obj = null; PROPSPEC[] propSpecs = new PROPSPEC[1]; PROPVARIANT[] vals = new PROPVARIANT[1]; propSpecs[0].propType = (uint)PropSpecType.Id; propSpecs[0].union.propId = propId; VARTYPE vtExpected = GetVtFromPropId(fmtid, propId); int hresult = ps.ReadMultiple(1, propSpecs, vals); if (hresult == SafeNativeCompoundFileConstants.S_OK) { try { if (vals[0].vt != vtExpected) { throw new FileFormatException( SR.Get( SRID.WrongDocumentPropertyVariantType, propId, fmtid.ToString(), vals[0].vt, vtExpected ) ); } switch (vals[0].vt) { case VARTYPE.VT_LPSTR: // // We store string properties as CP_ACP or UTF-8. // But no matter which format the string was encoded, we always use the UTF-8 // encoder/decoder to decode the byte array, because the UTF-8 code of an ASCII // string is the same as the ASCII string. // IntPtr pszVal = vals[0].union.pszVal; // // Because both the ASCII string and UTF-8 encoded string (byte array) are // stored in a memory block (pszVal) terminated by null, we can use // Marshal.PtrToStringAnsi(pszVal) to convert the memory block pointed by // pszVal to a string. Then from the string.Length, we can get the number of // bytes in the memory block. Otherwise, we cannot easily tell how many bytes // are stored in pszVal without an extra parameter. // string ansiString = Marshal.PtrToStringAnsi(pszVal); int nLen = ansiString.Length; byte[] byteArray = new byte[nLen]; Marshal.Copy(pszVal, byteArray, 0, nLen); obj = UTF8Encoding.UTF8.GetString(byteArray); break; case VARTYPE.VT_FILETIME: // // DateTime doesn't have a conversion from FILETIME. It has a // misleadingly named "FromFileTime" method that actually wants // a long. So... // obj = new Nullable <DateTime>(DateTime.FromFileTime(vals[0].union.hVal)); break; default: throw new FileFormatException( SR.Get(SRID.InvalidDocumentPropertyVariantType, vals[0].vt)); } } finally { #pragma warning suppress 6031 // suppressing a "by design" ignored return value SafeNativeCompoundFileMethods.SafePropVariantClear(ref vals[0]); } } else if (hresult == SafeNativeCompoundFileConstants.S_FALSE) { // Do nothing -- return the null object reference. } else { SecurityHelper.ThrowExceptionForHR(hresult); } return(obj); }