Related
I'm working on a WPF app which allows user to drag and drop files from Windows Explorer. For normal files, I'm able to access the path using
string[] fileNames = (string[])e.Data.GetData(DataFormats.FileDrop, false);
But for WPD files, its returning null. Have tried to follow the solution given in http://us.generation-nt.com/answer/drag-drop-pictures-wpd-camera-help-31497882.html#r , but i couldn't make it work. I'm getting AccessVoilationException when trying to get the count of items from the Shell array. I have posted the question in MSDN(http://social.msdn.microsoft.com/Forums/vstudio/en-US/ef7fc152-dd1b-4774-adb7-47b48726daea/drag-drop-from-windows-portable-device-to-wpf-application?forum=wpf), but didn't get any leads.
Is there something that I'm missing here? Could you please help me solve this issue?
Following is the relevant part my code.
public enum SIGDN : uint
{
NORMALDISPLAY = 0,
PARENTRELATIVEPARSING = 0x80018001,
PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
DESKTOPABSOLUTEPARSING = 0x80028000,
PARENTRELATIVEEDITING = 0x80031001,
DESKTOPABSOLUTEEDITING = 0x8004c000,
FILESYSPATH = 0x80058000,
URL = 0x80068000
}
internal class IIDGuid
{
private IIDGuid() { } // Avoid FxCop violation AvoidUninstantiatedInternalClasses
// IID GUID strings for relevant COM interfaces
internal const string IModalWindow = "b4db1657-70d7-485e-8e3e-6fcb5a5c1802";
internal const string IFileDialog = "42f85136-db7e-439c-85f1-e4075d135fc8";
internal const string IFileOpenDialog = "d57c7288-d4ad-4768-be02-9d969532d960";
internal const string IFileSaveDialog = "84bccd23-5fde-4cdb-aea4-af64b83d78ab";
internal const string IFileDialogEvents = "973510DB-7D7F-452B-8975-74A85828D354";
internal const string IShellItem = "43826D1E-E718-42EE-BC55-A1E261C37BFE";
internal const string IShellItemArray = "B63EA76D-1F85-456F-A19C-48159EFA858B";
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
public interface IShellItem
{
void BindToHandler(IntPtr pbc,
[MarshalAs(UnmanagedType.LPStruct)]Guid bhid,
[MarshalAs(UnmanagedType.LPStruct)]Guid riid,
out IntPtr ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
void Compare(IShellItem psi, uint hint, out int piOrder);
};
[ComImport]
[Guid(IIDGuid.IShellItemArray)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItemArray
{
// Not supported: IBindCtx
void BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid rbhid, [In] ref Guid riid, out IntPtr ppvOut);
void GetPropertyStore([In] int Flags, [In] ref Guid riid, out IntPtr ppv);
void GetCount(out uint pdwNumItems);
void GetItemAt([In] uint dwIndex, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);
void EnumItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenumShellItems);
}
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern int SHCreateShellItemArrayFromDataObject(
System.Runtime.InteropServices.ComTypes.IDataObject pdo,
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppv);
[DllImport("kernel32.dll", SetLastError = true)]
static extern Int32 GetLastError();
private void OnFileDrop(object sender, DragEventArgs e)
{
string[] fileNames = (string[])e.Data.GetData(DataFormats.FileDrop, false);// null
System.Runtime.InteropServices.ComTypes.IDataObject mydata = e.Data as System.Runtime.InteropServices.ComTypes.IDataObject;
IShellItemArray nativeShellItemArray;
Guid guid = new Guid(IIDGuid.IShellItemArray);
int retCode = SHCreateShellItemArrayFromDataObject(mydata, ref guid, out nativeShellItemArray);
IShellItem nativeShellItem;
if (retCode == 0)
{
IntPtr displayname;
uint items = 0;
try
{
nativeShellItemArray.GetCount(out items); //Getting AccessVoilationException in this line
}
catch (Exception ex)
{
}
if (items > 0)
{
for (uint item = 0; item < items; item++)
{
nativeShellItemArray.GetItemAt(item, out nativeShellItem);
nativeShellItem.GetDisplayName(SIGDN.DESKTOPABSOLUTEPARSING, out displayname);
//Do something
}
}
}
}
Given the following C# server code to start a DCOM server:
using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
using System.Runtime.InteropServices;
namespace Test
{
//
// .NET class, interface exposed through DCOM
//
// exposed COM interface
[GuidAttribute(MyService.guidIMyInterface), ComVisible(true)]
public interface IMyInterface
{
string GetDateTime(string prefix);
}
// exposed COM class
[GuidAttribute(MyService.guidMyClass), ComVisible(true)]
public class CMyClass: IMyInterface
{
// Print date & time and the current EXE name
public string GetDateTime(string prefix)
{
Process currentProcess = Process.GetCurrentProcess();
return string.Format("{0}: {1} [server-side COM call executed on {2}]",
prefix, DateTime.Now, currentProcess.MainModule.ModuleName);
}
}
//
// My hosting Windows service
//
internal class MyService :
ServiceBase
{
public MyService()
{
// Initialize COM security
Thread.CurrentThread.ApartmentState = ApartmentState.STA;
UInt32 hResult = ComAPI.CoInitializeSecurity(
IntPtr.Zero, // Add here your Security descriptor
-1,
IntPtr.Zero,
IntPtr.Zero,
ComAPI.RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
ComAPI.RPC_C_IMP_LEVEL_IDENTIFY,
IntPtr.Zero,
ComAPI.EOAC_DISABLE_AAA
| ComAPI.EOAC_SECURE_REFS
| ComAPI.EOAC_NO_CUSTOM_MARSHAL,
IntPtr.Zero);
if (hResult != 0)
throw new ApplicationException(
"CoIntializeSecurity failed" + hResult.ToString("X"));
}
// The main entry point for the process
static void Main()
{
ServiceBase.Run(new ServiceBase[] { new MyService() });
}
///
/// On start, register the COM class factory
///
protected override void OnStart(string[] args)
{
Guid CLSID_MyObject = new Guid(MyService.guidMyClass);
UInt32 hResult = ComAPI.CoRegisterClassObject(
ref CLSID_MyObject,
new MyClassFactory(),
ComAPI.CLSCTX_LOCAL_SERVER,
ComAPI.REGCLS_MULTIPLEUSE,
out _cookie);
if (hResult != 0)
throw new ApplicationException(
"CoRegisterClassObject failed" + hResult.ToString("X"));
}
///
/// On stop, remove the COM class factory registration
///
protected override void OnStop()
{
if (_cookie != 0)
ComAPI.CoRevokeClassObject(_cookie);
}
private int _cookie = 0;
//
// Public constants
//
public const string serviceName = "MyService";
public const string guidIMyInterface = "e88d15a5-0510-4115-9aee-a8421c96decb";
public const string guidMyClass = "f681abd0-41de-46c8-9ed3-d0f4eba19891";
}
//
// Standard installer
//
[RunInstaller(true)]
public class MyServiceInstaller :
System.Configuration.Install.Installer
{
public MyServiceInstaller()
{
processInstaller = new ServiceProcessInstaller();
serviceInstaller = new ServiceInstaller();
// Add a new service running under Local SYSTEM
processInstaller.Account = ServiceAccount.LocalSystem;
serviceInstaller.StartType = ServiceStartMode.Manual;
serviceInstaller.ServiceName = MyService.serviceName;
Installers.Add(serviceInstaller);
Installers.Add(processInstaller);
}
private ServiceInstaller serviceInstaller;
private ServiceProcessInstaller processInstaller;
}
//
// Internal COM Stuff
//
///
/// P/Invoke calls
///
internal class ComAPI
{
[DllImport("OLE32.DLL")]
public static extern UInt32 CoInitializeSecurity(
IntPtr securityDescriptor,
Int32 cAuth,
IntPtr asAuthSvc,
IntPtr reserved,
UInt32 AuthLevel,
UInt32 ImpLevel,
IntPtr pAuthList,
UInt32 Capabilities,
IntPtr reserved3
);
[DllImport ("ole32.dll")]
public static extern UInt32 CoRegisterClassObject (
ref Guid rclsid,
[MarshalAs (UnmanagedType.Interface)]IClassFactory pUnkn,
int dwClsContext,
int flags,
out int lpdwRegister);
[DllImport ("ole32.dll")]
public static extern UInt32 CoRevokeClassObject (int dwRegister);
public const int RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6; // Encrypted DCOM communication
public const int RPC_C_IMP_LEVEL_IDENTIFY = 2; // No impersonation really required
public const int CLSCTX_LOCAL_SERVER = 4;
public const int REGCLS_MULTIPLEUSE = 1;
public const int EOAC_DISABLE_AAA = 0x1000; // Disable Activate-as-activator
public const int EOAC_NO_CUSTOM_MARSHAL = 0x2000; // Disable custom marshalling
public const int EOAC_SECURE_REFS = 0x2; // Enable secure DCOM references
public const int CLASS_E_NOAGGREGATION = unchecked((int)0x80040110);
public const int E_NOINTERFACE = unchecked((int)0x80004002);
public const string guidIClassFactory = "00000001-0000-0000-C000-000000000046";
public const string guidIUnknown = "00000000-0000-0000-C000-000000000046";
}
///
/// IClassFactory declaration
///
[ComImport (), InterfaceType (ComInterfaceType.InterfaceIsIUnknown),
Guid (ComAPI.guidIClassFactory)]
internal interface IClassFactory
{
[PreserveSig]
int CreateInstance (IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject);
[PreserveSig]
int LockServer (bool fLock);
}
///
/// My Class factory implementation
///
internal class MyClassFactory : IClassFactory
{
public int CreateInstance (IntPtr pUnkOuter,
ref Guid riid,
out IntPtr ppvObject)
{
ppvObject = IntPtr.Zero;
if (pUnkOuter != IntPtr.Zero)
Marshal.ThrowExceptionForHR (ComAPI.CLASS_E_NOAGGREGATION);
if (riid == new Guid(MyService.guidIMyInterface)
|| riid == new Guid(ComAPI.guidIUnknown))
{
//
// Create the instance of my .NET object
//
ppvObject = Marshal.GetComInterfaceForObject(
new CMyClass(), typeof(IMyInterface));
}
else
Marshal.ThrowExceptionForHR (ComAPI.E_NOINTERFACE);
return 0;
}
public int LockServer (bool lockIt)
{
return 0;
}
}
}
Which we assume is compiled as a dll and registered with regasm and then called with the following VBS code *locally:
Dim obj
Set obj = CreateObject( "Test.CMyClass" )
wscript.echo obj.GetDateTime("Current date: ")
Now assume I want to run this vbs code on another machine and call the DCOM server remotely. What do I need to change?
My question is: How to write a DCOM server in C# that you can call remotely? (Assuming all the above steps have been done)
You use the Component Services MMC, or DCOMCnfg.exe, to configure it for remote access.
Then you can create it using the two-argument form of CreateObject.
Set obj = CreateObject( "Test.CMyClass", "SERVERNAME" )
I am developing a c# application and I a need to extract a cab file.
I couldn't find a library that does that in C# ) I cannot use Microsoft.Deployment.Compression.Cab.dll because of a licensing issue.
I found this code, but the problem is that when I use it I am able to find and extract only the first file in the cabinet.
OutputFileClose is called only if OutputFileOpen returns something either then IntPtr.Zero.
but if OutputFileClose is calles, then the enumeration is stopped.
So for this code OutputFileClose can be called only for one file
Can someone please help me figuring out how to write a code that will extract all the files?
I found out that Microsoft.Deployment.Compression.cab DLL can also be obtained from here
if you look at previous versions such as version 3.5 you will see that they were licensed with Common Public License Version 1.0 (CPL).
It seems that only in later versions the license was changes to MS-RL.
I was also able to create a solution of my own, but it is not optimal( I stopped working on it since I found that I can use Microsoft.Deployment.Compression.cab).
This is the code:
public class CabExtractor : IDisposable
{
private static class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
internal class CabError //Cabinet API: "ERF"
{
public int erfOper;
public int erfType;
public int fError;
}
[StructLayout(LayoutKind.Sequential)]
internal class FdiNotification //Cabinet API: "FDINOTIFICATION"
{
internal int cb;
//not sure if this should be a IntPtr or a strong
internal IntPtr psz1;
internal IntPtr psz2;
internal IntPtr psz3;
internal IntPtr pv;
internal IntPtr hf;
internal short date;
internal short time;
internal short attribs;
internal short setID;
internal short iCabinet;
internal short iFolder;
internal int fdie;
}
internal enum FdiNotificationType
{
CabinetInfo,
PartialFile,
CopyFile,
CloseFileInfo,
NextCabinet,
Enumerate
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr FdiMemAllocDelegate(int numBytes);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void FdiMemFreeDelegate(IntPtr mem);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr FdiFileOpenDelegate(string fileName, int oflag, int pmode);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 FdiFileReadDelegate(IntPtr hf,
[In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2,
ArraySubType = UnmanagedType.U1)] byte[] buffer, int cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 FdiFileWriteDelegate(IntPtr hf,
[In] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2,
ArraySubType = UnmanagedType.U1)] byte[] buffer, int cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 FdiFileCloseDelegate(IntPtr hf);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 FdiFileSeekDelegate(IntPtr hf, int dist, int seektype);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr FdiNotifyDelegate(
FdiNotificationType fdint, [In] [MarshalAs(UnmanagedType.LPStruct)] FdiNotification fdin);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "FDICreate", CharSet = CharSet.Ansi)]
internal static extern IntPtr FdiCreate(
FdiMemAllocDelegate fnMemAlloc,
FdiMemFreeDelegate fnMemFree,
FdiFileOpenDelegate fnFileOpen,
FdiFileReadDelegate fnFileRead,
FdiFileWriteDelegate fnFileWrite,
FdiFileCloseDelegate fnFileClose,
FdiFileSeekDelegate fnFileSeek,
int cpuType,
[MarshalAs(UnmanagedType.LPStruct)] CabError erf);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "FDIDestroy", CharSet = CharSet.Ansi)]
internal static extern bool FdiDestroy(IntPtr hfdi);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "FDICopy", CharSet = CharSet.Ansi)]
internal static extern bool FdiCopy(
IntPtr hfdi,
string cabinetName,
string cabinetPath,
int flags,
FdiNotifyDelegate fnNotify,
IntPtr fnDecrypt,
IntPtr userData);
}
internal class ArchiveFile
{
public IntPtr Handle { get; set; }
public string Name { get; set; }
public bool Found { get; set; }
public int Length { get; set; }
public byte[] Data { get; set; }
}
#region fields and properties
/// Very important!
/// Do not try to call directly to this methods, instead use the delegates. if you use them directly it may cause application crashes, corruption and data loss.
/// Using fields to save the delegate so that the delegate won't be garbage collected !
/// When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
private readonly NativeMethods.FdiMemAllocDelegate _fdiAllocMemHandler;
private readonly NativeMethods.FdiMemFreeDelegate _fdiFreeMemHandler;
private readonly NativeMethods.FdiFileOpenDelegate _fdiOpenStreamHandler;
private readonly NativeMethods.FdiFileReadDelegate _fdiReadStreamHandler;
private readonly NativeMethods.FdiFileWriteDelegate _fdiWriteStreamHandler;
private readonly NativeMethods.FdiFileCloseDelegate _fdiCloseStreamHandler;
private readonly NativeMethods.FdiFileSeekDelegate _fdiSeekStreamHandler;
private ArchiveFile _currentFileToDecompress;
readonly List<string> _fileNames = new List<string>();
private readonly NativeMethods.CabError _erf;
private const int CpuTypeUnknown = -1;
private readonly byte[] _inputData;
private bool _disposed;
/// <summary>
///
/// </summary>
private readonly List<string> _subDirectoryToIgnore = new List<string>();
/// <summary>
/// Path to the folder where the files will be extracted to
/// </summary>
private readonly string _extractionFolderPath;
/// <summary>
/// The name of the folder where the files will be extracted to
/// </summary>
public const string ExtractedFolderName = "ExtractedFiles";
public const string CabFileName = "setup.cab";
#endregion
public CabExtractor(string cabFilePath, IEnumerable<string> subDirectoryToUnpack)
: this(cabFilePath)
{
if (subDirectoryToUnpack != null)
_subDirectoryToIgnore.AddRange(subDirectoryToUnpack);
}
public CabExtractor(string cabFilePath)
{
var cabBytes =
File.ReadAllBytes(cabFilePath);
_inputData = cabBytes;
var cabFileLocation = Path.GetDirectoryName(cabFilePath) ?? "";
_extractionFolderPath = Path.Combine(cabFileLocation, ExtractedFolderName);
_erf = new NativeMethods.CabError();
FdiContext = IntPtr.Zero;
_fdiAllocMemHandler = MemAlloc;
_fdiFreeMemHandler = MemFree;
_fdiOpenStreamHandler = InputFileOpen;
_fdiReadStreamHandler = FileRead;
_fdiWriteStreamHandler = FileWrite;
_fdiCloseStreamHandler = InputFileClose;
_fdiSeekStreamHandler = FileSeek;
FdiContext = FdiCreate(_fdiAllocMemHandler, _fdiFreeMemHandler, _fdiOpenStreamHandler, _fdiReadStreamHandler, _fdiWriteStreamHandler, _fdiCloseStreamHandler, _fdiSeekStreamHandler, _erf);
}
public bool ExtractCabFiles()
{
if (!FdiIterate())
{
throw new Exception("Failed to iterate cab files");
}
foreach (var file in _fileNames)
{
ExtractFile(file);
}
return true;
}
private void ExtractFile(string fileName)
{
try
{
_currentFileToDecompress = new ArchiveFile { Name = fileName };
FdiCopy();
CreateAllRelevantDirectories(fileName);
if (_currentFileToDecompress.Data != null)
{
File.WriteAllBytes(Path.Combine(_extractionFolderPath, _currentFileToDecompress.Name), _currentFileToDecompress.Data);
}
}
catch (Exception ex)
{
SbaLogger.Instance.Error(ex);
SbaLogger.Instance.Error(string.Format("Failed to cextract file file {0}", fileName));
}
}
private void CreateAllRelevantDirectories(string filePath)
{
try
{
if (!Directory.Exists(_extractionFolderPath))
{
Directory.CreateDirectory(_extractionFolderPath);
}
var fullPathToFile = Path.GetDirectoryName(filePath);
if (fullPathToFile != null &&
!Directory.Exists(Path.Combine(_extractionFolderPath, fullPathToFile)))
{
Directory.CreateDirectory(Path.Combine(_extractionFolderPath, fullPathToFile));
}
}
catch (Exception ex)
{
SbaLogger.Instance.Error(ex);
SbaLogger.Instance.Error(string.Format("Failed to create directories for the file {0}",filePath));
}
}
private static string GetFileName(NativeMethods.FdiNotification notification)
{
var encoding = ((int)notification.attribs & 128) != 0 ? Encoding.UTF8 : Encoding.Default;
int length = 0;
while (Marshal.ReadByte(notification.psz1, length) != 0)
checked { ++length; }
var numArray = new byte[length];
Marshal.Copy(notification.psz1, numArray, 0, length);
string path = encoding.GetString(numArray);
if (Path.IsPathRooted(path))
path = path.Replace(String.Concat(Path.VolumeSeparatorChar), "");
return path;
}
private IntPtr ExtractCallback(NativeMethods.FdiNotificationType fdint, NativeMethods.FdiNotification fdin)
{
switch (fdint)
{
case NativeMethods.FdiNotificationType.CopyFile:
return CopyFiles(fdin);
case NativeMethods.FdiNotificationType.CloseFileInfo:
return OutputFileClose(fdin);
default:
return IntPtr.Zero;
}
}
private IntPtr IterateCallback(NativeMethods.FdiNotificationType fdint, NativeMethods.FdiNotification fdin)
{
switch (fdint)
{
case NativeMethods.FdiNotificationType.CopyFile:
return OutputFileOpen(fdin);
default:
return IntPtr.Zero;
}
}
private IntPtr InputFileOpen(string fileName, int oflag, int pmode)
{
var stream = new MemoryStream(_inputData);
GCHandle gch = GCHandle.Alloc(stream);
return (IntPtr)gch;
}
private int InputFileClose(IntPtr hf)
{
var stream = StreamFromHandle(hf);
stream.Close();
((GCHandle)(hf)).Free();
return 0;
}
/// <summary>
/// Copies the contents of input to output. Doesn't close either stream.
/// </summary>
public static void CopyStream(Stream input, Stream output)
{
var buffer = new byte[8 * 1024];
int len;
while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
private IntPtr CopyFiles(NativeMethods.FdiNotification fdin)
{
var fileName = GetFileName(fdin);
var extractFile = _currentFileToDecompress.Name == fileName ? _currentFileToDecompress : null;
if (extractFile != null)
{
var stream = new MemoryStream();
GCHandle gch = GCHandle.Alloc(stream);
extractFile.Handle = (IntPtr)gch;
return extractFile.Handle;
}
//Do not extract this file
return IntPtr.Zero;
}
private IntPtr OutputFileOpen(NativeMethods.FdiNotification fdin)
{
try
{
var extractFile = new ArchiveFile { Name = GetFileName(fdin) };
if (ShouldIgnoreFile(extractFile))
{
//ignore this file.
return IntPtr.Zero;
}
var stream = new MemoryStream();
GCHandle gch = GCHandle.Alloc(stream);
extractFile.Handle = (IntPtr)gch;
AddToListOfFiles(extractFile);
}
catch (Exception ex)
{
SbaLogger.Instance.Verbose(ex);
}
//return IntPtr.Zero so that the iteration will keep on going
return IntPtr.Zero;
}
private bool ShouldIgnoreFile(ArchiveFile extractFile)
{
var rootFolder = GetFileRootFolder(extractFile.Name);
return _subDirectoryToIgnore.Any(dir => dir.Equals(rootFolder, StringComparison.InvariantCultureIgnoreCase));
}
private string GetFileRootFolder(string path)
{
try
{
return path.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
}
catch (Exception)
{
return string.Empty;
}
}
private void AddToListOfFiles(ArchiveFile extractFile)
{
if (!_fileNames.Any(file => file.Equals(extractFile.Name)))
{
_fileNames.Add(extractFile.Name);
}
}
private IntPtr OutputFileClose(NativeMethods.FdiNotification fdin)
{
var extractFile = _currentFileToDecompress.Handle == fdin.hf ? _currentFileToDecompress : null;
var stream = StreamFromHandle(fdin.hf);
if (extractFile != null)
{
extractFile.Found = true;
extractFile.Length = (int)stream.Length;
if (stream.Length > 0)
{
extractFile.Data = new byte[stream.Length];
stream.Position = 0;
stream.Read(extractFile.Data, 0, (int)stream.Length);
}
}
stream.Close();
return IntPtr.Zero;
}
private static IntPtr FdiCreate(
NativeMethods.FdiMemAllocDelegate fnMemAlloc,
NativeMethods.FdiMemFreeDelegate fnMemFree,
NativeMethods.FdiFileOpenDelegate fnFileOpen,
NativeMethods.FdiFileReadDelegate fnFileRead,
NativeMethods.FdiFileWriteDelegate fnFileWrite,
NativeMethods.FdiFileCloseDelegate fnFileClose,
NativeMethods.FdiFileSeekDelegate fnFileSeek,
NativeMethods.CabError erf)
{
return NativeMethods.FdiCreate(fnMemAlloc, fnMemFree, fnFileOpen, fnFileRead, fnFileWrite,
fnFileClose, fnFileSeek, CpuTypeUnknown, erf);
}
private static int FileRead(IntPtr hf, byte[] buffer, int cb)
{
var stream = StreamFromHandle(hf);
return stream.Read(buffer, 0, cb);
}
private static int FileWrite(IntPtr hf, byte[] buffer, int cb)
{
var stream = StreamFromHandle(hf);
stream.Write(buffer, 0, cb);
return cb;
}
private static Stream StreamFromHandle(IntPtr hf)
{
return (Stream)((GCHandle)hf).Target;
}
private IntPtr MemAlloc(int cb)
{
return Marshal.AllocHGlobal(cb);
}
private void MemFree(IntPtr mem)
{
Marshal.FreeHGlobal(mem);
}
private int FileSeek(IntPtr hf, int dist, int seektype)
{
var stream = StreamFromHandle(hf);
return (int)stream.Seek(dist, (SeekOrigin)seektype);
}
private bool FdiCopy()
{
try
{
return NativeMethods.FdiCopy(FdiContext, "<notused>", "<notused>", 0, ExtractCallback, IntPtr.Zero, IntPtr.Zero);
}
catch (Exception)
{
return false;
}
}
private bool FdiIterate()
{
return NativeMethods.FdiCopy(FdiContext, "<notused>", "<notused>", 0, IterateCallback, IntPtr.Zero, IntPtr.Zero);
}
private IntPtr FdiContext { get; set; }
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{
if (!_disposed)
{
if (FdiContext != IntPtr.Zero)
{
NativeMethods.FdiDestroy(FdiContext);
FdiContext = IntPtr.Zero;
}
_disposed = true;
}
}
}
}
Have you tried exploring the 7zip sdk http://www.7-zip.org/sdk.html
Try this codeproject article - http://www.codeproject.com/Articles/15397/Cabinet-File-CAB-Compression-and-Extraction
Memory leak in the accepted answer.
method:
private IntPtr OutputFileClose(NativeMethods.FdiNotification fdin)
should call:
GCHandle.FromIntPtr(fdin.hf).Free();
I can't seem to find an answer to this.
Does anyone know?
Thanks
Here is the code of a utility class that allows you to deactivate WPF's WebBrowser context menu. It also allows you to suppress script errors (WPF WebBrowser control - how to supress script errors?) and change IE's DOCHOSTUIFLAG.
Usage sample:
public partial class Player : Window
{
private WebBrowserHostUIHandler _wbHandler;
public Player()
{
InitializeComponent();
...
_wbHandler = new WebBrowserHostUIHandler(MyWebBrower);
_wbHandler.IsWebBrowserContextMenuEnabled = false;
}
}
Utility code:
public class WebBrowserHostUIHandler : Native.IDocHostUIHandler
{
private const uint E_NOTIMPL = 0x80004001;
private const uint S_OK = 0;
private const uint S_FALSE = 1;
public WebBrowserHostUIHandler(WebBrowser browser)
{
if (browser == null)
throw new ArgumentNullException("browser");
Browser = browser;
browser.LoadCompleted += OnLoadCompleted;
browser.Navigated += OnNavigated;
IsWebBrowserContextMenuEnabled = true;
Flags |= HostUIFlags.ENABLE_REDIRECT_NOTIFICATION;
}
public WebBrowser Browser { get; private set; }
public HostUIFlags Flags { get; set; }
public bool IsWebBrowserContextMenuEnabled { get; set; }
public bool ScriptErrorsSuppressed { get; set; }
private void OnNavigated(object sender, NavigationEventArgs e)
{
SetSilent(Browser, ScriptErrorsSuppressed);
}
private void OnLoadCompleted(object sender, NavigationEventArgs e)
{
Native.ICustomDoc doc = Browser.Document as Native.ICustomDoc;
if (doc != null)
{
doc.SetUIHandler(this);
}
}
uint Native.IDocHostUIHandler.ShowContextMenu(int dwID, Native.POINT pt, object pcmdtReserved, object pdispReserved)
{
return IsWebBrowserContextMenuEnabled ? S_FALSE : S_OK;
}
uint Native.IDocHostUIHandler.GetHostInfo(ref Native.DOCHOSTUIINFO info)
{
info.dwFlags = (int)Flags;
info.dwDoubleClick = 0;
return S_OK;
}
uint Native.IDocHostUIHandler.ShowUI(int dwID, object activeObject, object commandTarget, object frame, object doc)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.HideUI()
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.UpdateUI()
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.EnableModeless(bool fEnable)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.OnDocWindowActivate(bool fActivate)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.OnFrameWindowActivate(bool fActivate)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.ResizeBorder(Native.COMRECT rect, object doc, bool fFrameWindow)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.TranslateAccelerator(ref System.Windows.Forms.Message msg, ref Guid group, int nCmdID)
{
return S_FALSE;
}
uint Native.IDocHostUIHandler.GetOptionKeyPath(string[] pbstrKey, int dw)
{
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.GetDropTarget(object pDropTarget, out object ppDropTarget)
{
ppDropTarget = null;
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.GetExternal(out object ppDispatch)
{
ppDispatch = Browser.ObjectForScripting;
return S_OK;
}
uint Native.IDocHostUIHandler.TranslateUrl(int dwTranslate, string strURLIn, out string pstrURLOut)
{
pstrURLOut = null;
return E_NOTIMPL;
}
uint Native.IDocHostUIHandler.FilterDataObject(IDataObject pDO, out IDataObject ppDORet)
{
ppDORet = null;
return E_NOTIMPL;
}
public static void SetSilent(WebBrowser browser, bool silent)
{
Native.IOleServiceProvider sp = browser.Document as Native.IOleServiceProvider;
if (sp != null)
{
Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E");
object webBrowser;
sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out webBrowser);
if (webBrowser != null)
{
webBrowser.GetType().InvokeMember("Silent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.PutDispProperty, null, webBrowser, new object[] { silent });
}
}
}
}
internal static class Native
{
[ComImport, Guid("BD3F23C0-D43E-11CF-893B-00AA00BDCE1A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IDocHostUIHandler
{
[PreserveSig]
uint ShowContextMenu(int dwID, POINT pt, [MarshalAs(UnmanagedType.Interface)] object pcmdtReserved, [MarshalAs(UnmanagedType.Interface)] object pdispReserved);
[PreserveSig]
uint GetHostInfo(ref DOCHOSTUIINFO info);
[PreserveSig]
uint ShowUI(int dwID, [MarshalAs(UnmanagedType.Interface)] object activeObject, [MarshalAs(UnmanagedType.Interface)] object commandTarget, [MarshalAs(UnmanagedType.Interface)] object frame, [MarshalAs(UnmanagedType.Interface)] object doc);
[PreserveSig]
uint HideUI();
[PreserveSig]
uint UpdateUI();
[PreserveSig]
uint EnableModeless(bool fEnable);
[PreserveSig]
uint OnDocWindowActivate(bool fActivate);
[PreserveSig]
uint OnFrameWindowActivate(bool fActivate);
[PreserveSig]
uint ResizeBorder(COMRECT rect, [MarshalAs(UnmanagedType.Interface)] object doc, bool fFrameWindow);
[PreserveSig]
uint TranslateAccelerator(ref System.Windows.Forms.Message msg, ref Guid group, int nCmdID);
[PreserveSig]
uint GetOptionKeyPath([Out, MarshalAs(UnmanagedType.LPArray)] string[] pbstrKey, int dw);
[PreserveSig]
uint GetDropTarget([In, MarshalAs(UnmanagedType.Interface)] object pDropTarget, [MarshalAs(UnmanagedType.Interface)] out object ppDropTarget);
[PreserveSig]
uint GetExternal([MarshalAs(UnmanagedType.IDispatch)] out object ppDispatch);
[PreserveSig]
uint TranslateUrl(int dwTranslate, [MarshalAs(UnmanagedType.LPWStr)] string strURLIn, [MarshalAs(UnmanagedType.LPWStr)] out string pstrURLOut);
[PreserveSig]
uint FilterDataObject(IDataObject pDO, out IDataObject ppDORet);
}
[StructLayout(LayoutKind.Sequential)]
internal struct DOCHOSTUIINFO
{
public int cbSize;
public int dwFlags;
public int dwDoubleClick;
public IntPtr dwReserved1;
public IntPtr dwReserved2;
}
[StructLayout(LayoutKind.Sequential)]
internal struct COMRECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
internal class POINT
{
public int x;
public int y;
}
[ComImport, Guid("3050F3F0-98B5-11CF-BB82-00AA00BDCE0B"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ICustomDoc
{
[PreserveSig]
int SetUIHandler(IDocHostUIHandler pUIHandler);
}
[ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IOleServiceProvider
{
[PreserveSig]
uint QueryService([In] ref Guid guidService, [In] ref Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject);
}
}
[Flags]
public enum HostUIFlags
{
DIALOG = 0x00000001,
DISABLE_HELP_MENU = 0x00000002,
NO3DBORDER = 0x00000004,
SCROLL_NO = 0x00000008,
DISABLE_SCRIPT_INACTIVE = 0x00000010,
OPENNEWWIN = 0x00000020,
DISABLE_OFFSCREEN = 0x00000040,
FLAT_SCROLLBAR = 0x00000080,
DIV_BLOCKDEFAULT = 0x00000100,
ACTIVATE_CLIENTHIT_ONLY = 0x00000200,
OVERRIDEBEHAVIORFACTORY = 0x00000400,
CODEPAGELINKEDFONTS = 0x00000800,
URL_ENCODING_DISABLE_UTF8 = 0x00001000,
URL_ENCODING_ENABLE_UTF8 = 0x00002000,
ENABLE_FORMS_AUTOCOMPLETE = 0x00004000,
ENABLE_INPLACE_NAVIGATION = 0x00010000,
IME_ENABLE_RECONVERSION = 0x00020000,
THEME = 0x00040000,
NOTHEME = 0x00080000,
NOPICS = 0x00100000,
NO3DOUTERBORDER = 0x00200000,
DISABLE_EDIT_NS_FIXUP = 0x00400000,
LOCAL_MACHINE_ACCESS_CHECK = 0x00800000,
DISABLE_UNTRUSTEDPROTOCOL = 0x01000000,
HOST_NAVIGATES = 0x02000000,
ENABLE_REDIRECT_NOTIFICATION = 0x04000000,
USE_WINDOWLESS_SELECTCONTROL = 0x08000000,
USE_WINDOWED_SELECTCONTROL = 0x10000000,
ENABLE_ACTIVEX_INACTIVATE_MODE = 0x20000000,
DPI_AWARE = 0x40000000
}
Reference mshtml
using mshtml;
Then you can do this by accessing the document:
MainBrowser.Navigate("https://www.domain.com");
MainBrowser.LoadCompleted += (o, args) =>
{
var doc = (HTMLDocumentEvents2_Event) MainBrowser.Document;
doc.oncontextmenu += obj => false;
};
From the internal WebBrowserSite class:
int UnsafeNativeMethods.IDocHostUIHandler.ShowContextMenu(int dwID, NativeMethods.POINT pt, object pcmdtReserved, object pdispReserved)
{
// Returning S_FALSE will allow the native control to do default processing,
// i.e., execute the shortcut key. Returning S_OK will cancel the context menu
return NativeMethods.S_FALSE;
}
Hard-baked, you cannot override this. Putting the Winforms WebBrower inside a WindowsFormsHost would be a workaround, it's got a smarter implementation of this method that uses the IsWebBrowserContextMenuEnabled property and allows you to create your own context menu.
This is an old question, but I had a unique problem in that I wanted/needed to use the newer CSProj file format with the .Net Core SDK but it has a known issue with not being able to reference extension libraries, such as mshtml. So I finally found a solution that's possible without a reference to mshtml.
Some inspiration came from https://stackoverflow.com/a/28464764/2646868 where the user cast the WebBrowser.Document property to dynamic.
Assuming webView is the name of your WebBrowser control, hook up the LoadCompleted event like such:
private void webView_LoadCompleted(object sender, NavigationEventArgs e)
{
dynamic document = webView.Document;
document.oncontextmenu += new Func<bool>(() => false);
}
Digging graves it seems, but...
Based on e.g. answer here https://stackoverflow.com/a/6979961/4537127 get IWebBrowser2 interface, then get DWebBrowserEvents2_Event interface, and attach handler to DocumentComplete event
SHDocVw.DWebBrowserEvents2_Event wbEvents = (SHDocVw.DWebBrowserEvents2_Event)myWebBrowser2;
wbEvents.DocumentComplete += new SHDocVw.DWebBrowserEvents2_DocumentCompleteEventHandler(OnWebBrowserDocumentComplete);
(Above must to be done only once on WebBrowser LoadCompleted event, so add some logic to run it only on first one)
And then in this handler, run sone JavaScript to attach event listener to document, and as bonus, to the iframe in this document
private void OnWebBrowserDocumentComplete(object pDisp, ref object URL)
{
/* try-catch wrap this if you get e.g. DISP_E_EXCEPTION */
MyBrowser.InvokeScript("execScript", new Object[]
{
"document.addEventListener('contextmenu', function(e){ e.preventDefault(); }, false);" +
"var frm = document.getElementById('frm');" +
"frm.onload = function() { frm.contentDocument.body.addEventListener('contextmenu', function(e){ e.preventDefault(); }, false); };",
"JavaScript"
});
}
This will work even if the iframe content is changed. I use this with DITA WebHelp classic running in WPF WebBrowser.
And no mshtml reference needed.
I'm writing a program that needs to be able to extract the thumbnail image from a file. I've gotten a hold of a class, ThumbnailCreator, which is able to do this. The source of this Class is below.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using System.Drawing.Imaging;
internal class ThumbnailCreator : IDisposable
{
// Fields
private IMalloc alloc;
private Size desiredSize;
private bool disposed;
private static readonly string fileExtention = ".jpg";
private Bitmap thumbnail;
// Methods
public ThumbnailCreator()
{
this.desiredSize = new Size(100, 100);
}
public ThumbnailCreator(Size desiredSize)
{
this.desiredSize = new Size(100, 100);
this.DesiredSize = desiredSize;
}
public void Dispose()
{
if (!this.disposed)
{
if (this.alloc != null)
{
Marshal.ReleaseComObject(this.alloc);
}
this.alloc = null;
if (this.thumbnail != null)
{
this.thumbnail.Dispose();
}
this.disposed = true;
}
}
~ThumbnailCreator()
{
this.Dispose();
}
private bool getThumbNail(string file, IntPtr pidl, IShellFolder item)
{
bool CS;
IntPtr hBmp = IntPtr.Zero;
IExtractImage extractImage = null;
try
{
if (Path.GetFileName(PathFromPidl(pidl)).ToUpper().Equals(Path.GetFileName(file).ToUpper()))
{
int prgf;
IUnknown iunk = null;
Guid iidExtractImage = new Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1");
item.GetUIObjectOf(IntPtr.Zero, 1, ref pidl, ref iidExtractImage, out prgf, ref iunk);
extractImage = (IExtractImage) iunk;
if (extractImage != null)
{
SIZE sz = new SIZE {
cx = this.desiredSize.Width,
cy = this.desiredSize.Height
};
StringBuilder location = new StringBuilder(260, 260);
int priority = 0;
int requestedColourDepth = 0x20;
EIEIFLAG flags = EIEIFLAG.IEIFLAG_SCREEN | EIEIFLAG.IEIFLAG_ASPECT;
int uFlags = (int) flags;
extractImage.GetLocation(location, location.Capacity, ref priority, ref sz, requestedColourDepth, ref uFlags);
extractImage.Extract(out hBmp);
if (hBmp != IntPtr.Zero)
{
this.thumbnail = Image.FromHbitmap(hBmp);
}
Marshal.ReleaseComObject(extractImage);
extractImage = null;
}
return true;
}
CS = false;
}
catch (Exception)
{
if (hBmp != IntPtr.Zero)
{
UnManagedMethods.DeleteObject(hBmp);
}
if (extractImage != null)
{
Marshal.ReleaseComObject(extractImage);
}
throw;
}
return CS;
}
public Bitmap GetThumbNail(string file)
{
if (!File.Exists(file) && !Directory.Exists(file))
{
throw new FileNotFoundException(string.Format("The file '{0}' does not exist", file), file);
}
if (this.thumbnail != null)
{
this.thumbnail.Dispose();
this.thumbnail = null;
}
IShellFolder folder = getDesktopFolder;
if (folder != null)
{
IntPtr pidlMain;
try
{
int cParsed;
int pdwAttrib;
string filePath = Path.GetDirectoryName(file);
folder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, filePath, out cParsed, out pidlMain, out pdwAttrib);
}
catch (Exception)
{
Marshal.ReleaseComObject(folder);
throw;
}
if (pidlMain != IntPtr.Zero)
{
Guid iidShellFolder = new Guid("000214E6-0000-0000-C000-000000000046");
IShellFolder item = null;
try
{
folder.BindToObject(pidlMain, IntPtr.Zero, ref iidShellFolder, ref item);
}
catch (Exception)
{
Marshal.ReleaseComObject(folder);
this.Allocator.Free(pidlMain);
throw;
}
if (item != null)
{
IEnumIDList idEnum = null;
try
{
item.EnumObjects(IntPtr.Zero, ESHCONTF.SHCONTF_NONFOLDERS | ESHCONTF.SHCONTF_FOLDERS, ref idEnum);
}
catch (Exception)
{
Marshal.ReleaseComObject(folder);
this.Allocator.Free(pidlMain);
throw;
}
if (idEnum != null)
{
IntPtr pidl = IntPtr.Zero;
bool complete = false;
while (!complete)
{
int fetched;
if (idEnum.Next(1, ref pidl, out fetched) != 0)
{
pidl = IntPtr.Zero;
complete = true;
}
else if (this.getThumbNail(file, pidl, item))
{
complete = true;
}
if (pidl != IntPtr.Zero)
{
this.Allocator.Free(pidl);
}
}
Marshal.ReleaseComObject(idEnum);
}
Marshal.ReleaseComObject(item);
}
this.Allocator.Free(pidlMain);
}
Marshal.ReleaseComObject(folder);
}
return this.thumbnail;
}
private static string PathFromPidl(IntPtr pidl)
{
StringBuilder path = new StringBuilder(260, 260);
if (UnManagedMethods.SHGetPathFromIDList(pidl, path) != 0)
{
return path.ToString();
}
return string.Empty;
}
// Properties
private IMalloc Allocator
{
get
{
if (!this.disposed && (this.alloc == null))
{
UnManagedMethods.SHGetMalloc(out this.alloc);
}
return this.alloc;
}
}
public Size DesiredSize
{
get
{
return this.desiredSize;
}
set
{
this.desiredSize = value;
}
}
private static IShellFolder getDesktopFolder
{
get
{
IShellFolder ppshf;
UnManagedMethods.SHGetDesktopFolder(out ppshf);
return ppshf;
}
}
public Bitmap ThumbNail
{
get
{
return this.thumbnail;
}
}
// Nested Types
private enum EIEIFLAG
{
IEIFLAG_ASPECT = 4,
IEIFLAG_ASYNC = 1,
IEIFLAG_CACHE = 2,
IEIFLAG_GLEAM = 0x10,
IEIFLAG_NOBORDER = 0x100,
IEIFLAG_NOSTAMP = 0x80,
IEIFLAG_OFFLINE = 8,
IEIFLAG_ORIGSIZE = 0x40,
IEIFLAG_QUALITY = 0x200,
IEIFLAG_SCREEN = 0x20
}
[Flags]
private enum ESFGAO
{
SFGAO_CANCOPY = 1,
SFGAO_CANDELETE = 0x20,
SFGAO_CANLINK = 4,
SFGAO_CANMOVE = 2,
SFGAO_CANRENAME = 0x10,
SFGAO_CAPABILITYMASK = 0x177,
SFGAO_COMPRESSED = 0x4000000,
SFGAO_CONTENTSMASK = -2147483648,
SFGAO_DISPLAYATTRMASK = 0xf0000,
SFGAO_DROPTARGET = 0x100,
SFGAO_FILESYSANCESTOR = 0x10000000,
SFGAO_FILESYSTEM = 0x40000000,
SFGAO_FOLDER = 0x20000000,
SFGAO_GHOSTED = 0x80000,
SFGAO_HASPROPSHEET = 0x40,
SFGAO_HASSUBFOLDER = -2147483648,
SFGAO_LINK = 0x10000,
SFGAO_READONLY = 0x40000,
SFGAO_REMOVABLE = 0x2000000,
SFGAO_SHARE = 0x20000,
SFGAO_VALIDATE = 0x1000000
}
[Flags]
private enum ESHCONTF
{
SHCONTF_FOLDERS = 0x20,
SHCONTF_INCLUDEHIDDEN = 0x80,
SHCONTF_NONFOLDERS = 0x40
}
[Flags]
private enum ESHGDN
{
SHGDN_FORADDRESSBAR = 0x4000,
SHGDN_FORPARSING = 0x8000,
SHGDN_INFOLDER = 1,
SHGDN_NORMAL = 0
}
[Flags]
private enum ESTRRET
{
STRRET_WSTR,
STRRET_OFFSET,
STRRET_CSTR
}
[ComImport, Guid("000214F2-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IEnumIDList
{
[PreserveSig]
int Next(int celt, ref IntPtr rgelt, out int pceltFetched);
void Skip(int celt);
void Reset();
void Clone(ref ThumbnailCreator.IEnumIDList ppenum);
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1")]
private interface IExtractImage
{
void GetLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPathBuffer, int cch, ref int pdwPriority, ref ThumbnailCreator.SIZE prgSize, int dwRecClrDepth, ref int pdwFlags);
void Extract(out IntPtr phBmpThumbnail);
}
[ComImport, Guid("00000002-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IMalloc
{
[PreserveSig]
IntPtr Alloc(int cb);
[PreserveSig]
IntPtr Realloc(IntPtr pv, int cb);
[PreserveSig]
void Free(IntPtr pv);
[PreserveSig]
int GetSize(IntPtr pv);
[PreserveSig]
int DidAlloc(IntPtr pv);
[PreserveSig]
void HeapMinimize();
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214E6-0000-0000-C000-000000000046")]
private interface IShellFolder
{
void ParseDisplayName(IntPtr hwndOwner, IntPtr pbcReserved, [MarshalAs(UnmanagedType.LPWStr)] string lpszDisplayName, out int pchEaten, out IntPtr ppidl, out int pdwAttributes);
void EnumObjects(IntPtr hwndOwner, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF grfFlags, ref ThumbnailCreator.IEnumIDList ppenumIDList);
void BindToObject(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, ref ThumbnailCreator.IShellFolder ppvOut);
void BindToStorage(IntPtr pidl, IntPtr pbcReserved, ref Guid riid, IntPtr ppvObj);
[PreserveSig]
int CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2);
void CreateViewObject(IntPtr hwndOwner, ref Guid riid, IntPtr ppvOut);
void GetAttributesOf(int cidl, IntPtr apidl, [MarshalAs(UnmanagedType.U4)] ref ThumbnailCreator.ESFGAO rgfInOut);
void GetUIObjectOf(IntPtr hwndOwner, int cidl, ref IntPtr apidl, ref Guid riid, out int prgfInOut, ref ThumbnailCreator.IUnknown ppvOut);
void GetDisplayNameOf(IntPtr pidl, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHGDN uFlags, ref ThumbnailCreator.STRRET_CSTR lpName);
void SetNameOf(IntPtr hwndOwner, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string lpszName, [MarshalAs(UnmanagedType.U4)] ThumbnailCreator.ESHCONTF uFlags, ref IntPtr ppidlOut);
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000000-0000-0000-C000-000000000046")]
private interface IUnknown
{
[PreserveSig]
IntPtr QueryInterface(ref Guid riid, out IntPtr pVoid);
[PreserveSig]
IntPtr AddRef();
[PreserveSig]
IntPtr Release();
}
[StructLayout(LayoutKind.Sequential)]
private struct SIZE
{
public int cx;
public int cy;
}
[StructLayout(LayoutKind.Explicit, CharSet=CharSet.Auto)]
private struct STRRET_ANY
{
// Fields
[FieldOffset(4)]
public IntPtr pOLEString;
[FieldOffset(0)]
public ThumbnailCreator.ESTRRET uType;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto, Pack=4)]
private struct STRRET_CSTR
{
public ThumbnailCreator.ESTRRET uType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=520)]
public byte[] cStr;
}
private class UnManagedMethods
{
// Methods
[DllImport("gdi32", CharSet=CharSet.Auto)]
internal static extern int DeleteObject(IntPtr hObject);
[DllImport("shell32", CharSet=CharSet.Auto)]
internal static extern int SHGetDesktopFolder(out ThumbnailCreator.IShellFolder ppshf);
[DllImport("shell32", CharSet=CharSet.Auto)]
internal static extern int SHGetMalloc(out ThumbnailCreator.IMalloc ppMalloc);
[DllImport("shell32", CharSet=CharSet.Auto)]
internal static extern int SHGetPathFromIDList(IntPtr pidl, StringBuilder pszPath);
}
}
As a test I created a quick console application, the code of which is below. This test worked fine and was able to extract a thumbnail, and saved it to a PNG file.
static void Main(string[] args)
{
try
{
Console.WriteLine("press a key to extract");
System.Console.ReadKey();
string path = #"C:\somefile.xyz";
ThumbnailCreator creator = new ThumbnailCreator();
creator.DesiredSize = new Size(600, 600);
Bitmap bm = creator.GetThumbNail(path);
bm.Save(#"C:\blah.png", System.Drawing.Imaging.ImageFormat.Png);
Console.WriteLine("press a key to exit");
System.Console.ReadKey();
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
}
}
My problem is the real application I want to use this in runs as a plug-in for another application. When I try to run the same test code in the plug-in creator.GetThumbNail(path); returns null.
I debugged a little further and found that in the method ThumbnailCreator.getThumbNail(string file, IntPtr pidl,IshellFolder item) the line extractImage.Extract(out hBmp); returns IntPtr.Zero. Whereas in the console application that works this method actually returns a number. Maybe this is the problem? Or maybe this problem happens before this. Honestly, I'm completely lost when it comes to Interop and Windows API stuff.
Does anyone know of any possible reasons why this class would work in a standalone console application, but not as part of a plug-in for another application?
Update
Here's a bit more detail of how this plug-in is created. To add a custom command to this program you create a class that implements an ICommand interface from is API. The interface has a single Execute() method where code is place that should run when the user executes the custom command from the program. The native application, however, is what actualy instantiates my command class and calls the execute method.
What could be different about the way that the native app instantiates my command class and or calls the Execute() method that would prevent the thumbnail extraction from working?
Just ran across this issue myself and I do say, it took some time to figure out that it was a bitness issue. In your scenario your console app was probably compiled as x64 and your Windows app as x86.
The ExtractImage interface (BB2E617C-0920-11d1-9A0B-00C04FC2D6C1) on a 64bit OS is only accessible from 64bit applications (because explorer is 64bit), thus resulting in a "Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG))" exception in applications compiled as x86.
Code that calls the Shell API must be in a COM Single Threaded Apartment, try putting [STAThread] attribute on the thread that calls this.