Related
I successfully implemented the solution from How does one invoke the Windows Permissions dialog programmatically? and related: How to display file properties dialog security tab from c#.
public static bool ShowFileProperties(string Filename)
{
SHELLEXECUTEINFO info = new SHELLEXECUTEINFO();
info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
info.lpVerb = "properties";
info.lpFile = Filename;
info.lpParameters = "Security";
info.nShow = SW_SHOW;
info.fMask = SEE_MASK_INVOKEIDLIST;
return ShellExecuteEx(ref info);
}
However, I would like to get to the Permissions window as you would with the Edit.. button from that dialogue.
Any ideas?
You can click the button "Edit..." programmatically:
Edited example from here, matching to the window title seen in your Screenshot:
private void clickEdit()
{
// Look for a top-level window/application called "SupportAssist Properties"
var root = AutomationElement.RootElement;
var condition1 = new PropertyCondition(AutomationElement.NameProperty, "SupportAssist Properties");
var treeWalker = new TreeWalker(condition1);
AutomationElement element = treeWalker.GetFirstChild(root);
if (element == null)
{
// not found
return;
}
// great! window found
var window = element;
// now look for a button with the text "Edit..."
var buttonElement = window.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "Edit.."));
if (buttonElement == null)
{
// not found
return;
}
// great! now click the button
var invokePattern = buttonElement.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
invokePattern?.Invoke();
}
Important! The linked source of this code says the following:
var buttonElement = window.FindFirst(TreeScope.Children
But this does not work in the "Properties" window. It does not find "Edit..." in this level.
You have to search for it one level deeper:
var buttonElement = window.FindFirst(TreeScope.Descendants
What I know, you need to use EditSecurity function from aclui.dll and implement ISecurityInformation interface.
[DllImport("aclui.dll", CharSet = CharSet.Auto)]
static extern bool EditSecurity(IntPtr hwnd, ISecurityInformation psi);
Interface
[Guid("965FC360-16FF-11d0-91CB-00AA00BBB723")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ISecurityInformation
{
void GetObjectInformation([Out] out SI_OBJECT_INFO pObjectInfo);
void GetSecurity([In] uint RequestedInformation, [Out] out IntPtr ppSecurityDescriptor, [In] bool fDefault);
void SetSecurity([In] uint SecurityInformation, [In] IntPtr pSecurityDescriptor);
void GetAccessRights([In] IntPtr pguidObjectType, [In] uint dwFlags, [Out] out IntPtr ppAccess, [Out] out int pcAccesses, [Out] out int piDefaultAccess);
void MapGeneric([In] IntPtr pguidObjectType, [In] IntPtr pAceFlags, [In, Out] ref uint pMask);
void GetInheritTypes([Out] out IntPtr ppInheritTypes, [Out] out int pcInheritTypes);
void PropertySheetPageCallback([In] IntPtr hwnd, [In] uint uMsg, [In] uint uPage);
}
SI_OBJECT_INFO structure
[StructLayout(LayoutKind.Sequential)]
public struct SI_OBJECT_INFO
{
public uint dwFlags;
public IntPtr hInstance;
public IntPtr pszServerName;
public IntPtr pszObjectName;
public IntPtr pszPageTitle;
public Guid guidObjectType;
}
SecurityInformation class. Right now, I don't know how to implement all methods of ISecurityInformation.
public class SecurityInformation : ISecurityInformation
{
SecurityInformation()
{
}
void ISecurityInformation.GetObjectInformation(out SI_OBJECT_INFO pObjectInfo)
{
pObjectInfo = new SI_OBJECT_INFO()
{
// TODO
};
}
void ISecurityInformation.GetSecurity(uint RequestedInformation, out IntPtr ppSecurityDescriptor, bool fDefault)
{
// TODO
}
void ISecurityInformation.SetSecurity(uint SecurityInformation, IntPtr pSecurityDescriptor)
{
// TODO
}
void ISecurityInformation.GetAccessRights(IntPtr pguidObjectType, uint dwFlags, out IntPtr ppAccess, out int pcAccesses, out int piDefaultAccess)
{
// TODO
}
void ISecurityInformation.MapGeneric(IntPtr pguidObjectType, IntPtr pAceFlags, ref uint pMask)
{
// TODO
}
void ISecurityInformation.GetInheritTypes(out IntPtr ppInheritTypes, out int pcInheritTypes)
{
// TODO
}
void ISecurityInformation.PropertySheetPageCallback(IntPtr hwnd, uint uMsg, uint uPage)
{
// TODO
}
}
I am trying to launch an appcontainer using C# and pinvoke and getting hung up setting the security capabilities into the attribute list via UpdateProcThreadAttribute().
The GetLastError() returns
87 - invalid parameter
Similar c++ code from this article (github link at the bottom https://scorpiosoftware.net/2019/01/15/fun-with-appcontainers/) works well.
Code: (pinvoke structs/enums/externs below)
public bool testAppContainer()
{
const uint SE_GROUP_ENABLED = 0x00000004;
const int ProcThreadAttributeSecurityCapabilities = 0x00020009;
STARTUPINFOEX startupInfoEx = new STARTUPINFOEX();
PROCESSINFO pInfo = new PROCESSINFO();
SECURITY_CAPABILITIES sc = new SECURITY_CAPABILITIES();
IntPtr pSidInternetClientServer = IntPtr.Zero;
startupInfoEx.StartupInfo.cb = Marshal.SizeOf(startupInfoEx);
string appContainerName = "Test.AppContainer";
try
{
//get the size, then the sid
IntPtr clientServerSid = IntPtr.Zero; uint cbSid = 0;
SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, clientServerSid, pSidInternetClientServer, ref cbSid);
pSidInternetClientServer = Marshal.AllocCoTaskMem(Convert.ToInt32(cbSid));
if (!CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, clientServerSid, pSidInternetClientServer, ref cbSid))
{
throw new ApplicationException($"Unable to create well known Client Server capability sid!");
}
//create an array of one capability
SID_AND_ATTRIBUTES[] sidAndAttributes = new SID_AND_ATTRIBUTES[1];
sidAndAttributes[0].Sid = pSidInternetClientServer;
sidAndAttributes[0].Attributes = SE_GROUP_ENABLED;
sc.Capabilities = sidAndAttributes;
sc.CapabilityCount = 1;
IntPtr appConSid = IntPtr.Zero;
int hResult = AppContainerNative.DeriveAppContainerSidFromAppContainerName(appContainerName, out appConSid);
if (hResult < 0)
{
throw new ApplicationException($"Application container {appContainerName} was not found on the machine (err code {hResult})");
}
//security capabilities for existing app container
sc.AppContainerSid = appConSid;
const int reserved = 0; var size = IntPtr.Zero; int attributeCount = 1;
bool alreadyInit = ProcessNative.InitializeProcThreadAttributeList(IntPtr.Zero, attributeCount, reserved, ref size);
if (alreadyInit || size == IntPtr.Zero)
{
throw new Exception(string.Format("Couldn't get the size of the attribute list for {0} attributes", attributeCount));
}
startupInfoEx.lpAttributeList = Marshal.AllocHGlobal(size);
if (startupInfoEx.lpAttributeList == IntPtr.Zero)
{
throw new Exception("Couldn't reserve space for a new attribute list");
}
bool initAttributeList = ProcessNative.InitializeProcThreadAttributeList(startupInfoEx.lpAttributeList, attributeCount, reserved, ref size);
if (!initAttributeList )
{
throw new Exception("Couldn't create new attribute list");
}
//also tried this (pass in unmanagedAddr instead of ref sc)
// IntPtr unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(sc));
// Marshal.StructureToPtr(sc, unmanagedAddr, true);
bool success = UpdateProcThreadAttribute(startupInfoEx.lpAttributeList, reserved, (IntPtr)ProcThreadAttributeSecurityCapabilities,
ref sc, (IntPtr)Marshal.SizeOf(sc), IntPtr.Zero, IntPtr.Zero);
// Marshal.FreeHGlobal(unmanagedAddr);
// unmanagedAddr = IntPtr.Zero;
if (!success)
{
throw new Exception($"Error adding security capabilities to process launch. Error Code: {Marshal.GetLastWin32Error()}");
}
var pSec = new SECURITY_ATTRIBUTES(); pSec.nLength = Marshal.SizeOf(pSec);
var tSec = new SECURITY_ATTRIBUTES(); tSec.nLength = Marshal.SizeOf(tSec);
success = CreateProcess(null, #"c:\windows\notepad.exe", ref pSec, ref tSec, false,
(uint)ProcessNative.CreateProcessFlags.EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref startupInfoEx, out pInfo);
if (success)
{
System.Diagnostics.Debug.WriteLine($"Created new app container process {pInfo.dwProcessId}!");
return true;
}
}
finally
{
// Free the attribute list
if (startupInfoEx.lpAttributeList != IntPtr.Zero)
{
ProcessNative.DeleteProcThreadAttributeList(startupInfoEx.lpAttributeList);
Marshal.FreeHGlobal(startupInfoEx.lpAttributeList);
}
// Close process and thread handles
if (pInfo.hProcess != IntPtr.Zero)
{
ProcessNative.CloseHandle(pInfo.hProcess);
}
if (pInfo.hThread != IntPtr.Zero)
{
ProcessNative.CloseHandle(pInfo.hThread);
}
Marshal.FreeHGlobal(startupInfoEx.lpAttributeList);
if (pSidInternetClientServer != IntPtr.Zero)
Marshal.FreeCoTaskMem(pSidInternetClientServer);
}
return true;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_CAPABILITIES
{
public IntPtr AppContainerSid;
[MarshalAs(UnmanagedType.ByValArray)]
public SID_AND_ATTRIBUTES[] Capabilities;
public uint CapabilityCount;
public uint Reserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct SID_AND_ATTRIBUTES
{
public IntPtr Sid;
public UInt32 Attributes;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct PROCESSINFO
{
public IntPtr hProcess;
public IntPtr hThread;
public Int32 dwProcessId;
public Int32 dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
public enum WELL_KNOWN_SID_TYPE
{
WinNullSid,
WinWorldSid,
WinLocalSid,
WinCreatorOwnerSid,
WinCreatorGroupSid,
WinCreatorOwnerServerSid,
WinCreatorGroupServerSid,
WinNtAuthoritySid,
WinDialupSid,
WinNetworkSid,
WinBatchSid,
WinInteractiveSid,
WinServiceSid,
WinAnonymousSid,
WinProxySid,
WinEnterpriseControllersSid,
WinSelfSid,
WinAuthenticatedUserSid,
WinRestrictedCodeSid,
WinTerminalServerSid,
WinRemoteLogonIdSid,
WinLogonIdsSid,
WinLocalSystemSid,
WinLocalServiceSid,
WinNetworkServiceSid,
WinBuiltinDomainSid,
WinBuiltinAdministratorsSid,
WinBuiltinUsersSid,
WinBuiltinGuestsSid,
WinBuiltinPowerUsersSid,
WinBuiltinAccountOperatorsSid,
WinBuiltinSystemOperatorsSid,
WinBuiltinPrintOperatorsSid,
WinBuiltinBackupOperatorsSid,
WinBuiltinReplicatorSid,
WinBuiltinPreWindows2000CompatibleAccessSid,
WinBuiltinRemoteDesktopUsersSid,
WinBuiltinNetworkConfigurationOperatorsSid,
WinAccountAdministratorSid,
WinAccountGuestSid,
WinAccountKrbtgtSid,
WinAccountDomainAdminsSid,
WinAccountDomainUsersSid,
WinAccountDomainGuestsSid,
WinAccountComputersSid,
WinAccountControllersSid,
WinAccountCertAdminsSid,
WinAccountSchemaAdminsSid,
WinAccountEnterpriseAdminsSid,
WinAccountPolicyAdminsSid,
WinAccountRasAndIasServersSid,
WinNTLMAuthenticationSid,
WinDigestAuthenticationSid,
WinSChannelAuthenticationSid,
WinThisOrganizationSid,
WinOtherOrganizationSid,
WinBuiltinIncomingForestTrustBuildersSid,
WinBuiltinPerfMonitoringUsersSid,
WinBuiltinPerfLoggingUsersSid,
WinBuiltinAuthorizationAccessSid,
WinBuiltinTerminalServerLicenseServersSid,
WinBuiltinDCOMUsersSid,
WinBuiltinIUsersSid,
WinIUserSid,
WinBuiltinCryptoOperatorsSid,
WinUntrustedLabelSid,
WinLowLabelSid,
WinMediumLabelSid,
WinHighLabelSid,
WinSystemLabelSid,
WinWriteRestrictedCodeSid,
WinCreatorOwnerRightsSid,
WinCacheablePrincipalsGroupSid,
WinNonCacheablePrincipalsGroupSid,
WinEnterpriseReadonlyControllersSid,
WinAccountReadonlyControllersSid,
WinBuiltinEventLogReadersGroup,
WinNewEnterpriseReadonlyControllersSid,
WinBuiltinCertSvcDComAccessGroup,
WinMediumPlusLabelSid,
WinLocalLogonSid,
WinConsoleLogonSid,
WinThisOrganizationCertificateSid,
WinApplicationPackageAuthoritySid,
WinBuiltinAnyPackageSid,
WinCapabilityInternetClientSid,
WinCapabilityInternetClientServerSid,
WinCapabilityPrivateNetworkClientServerSid,
WinCapabilityPicturesLibrarySid,
WinCapabilityVideosLibrarySid,
WinCapabilityMusicLibrarySid,
WinCapabilityDocumentsLibrarySid,
WinCapabilitySharedUserCertificatesSid,
WinCapabilityEnterpriseAuthenticationSid,
WinCapabilityRemovableStorageSid,
WinBuiltinRDSRemoteAccessServersSid,
WinBuiltinRDSEndpointServersSid,
WinBuiltinRDSManagementServersSid,
WinUserModeDriversSid,
WinBuiltinHyperVAdminsSid,
WinAccountCloneableControllersSid,
WinBuiltinAccessControlAssistanceOperatorsSid,
WinBuiltinRemoteManagementUsersSid,
WinAuthenticationAuthorityAssertedSid,
WinAuthenticationServiceAssertedSid,
WinLocalAccountSid,
WinLocalAccountAndAdministratorSid,
WinAccountProtectedUsersSid,
WinCapabilityAppointmentsSid,
WinCapabilityContactsSid,
WinAccountDefaultSystemManagedSid,
WinBuiltinDefaultSystemManagedGroupSid,
WinBuiltinStorageReplicaAdminsSid,
WinAccountKeyAdminsSid,
WinAccountEnterpriseKeyAdminsSid,
WinAuthenticationKeyTrustSid,
WinAuthenticationKeyPropertyMFASid,
WinAuthenticationKeyPropertyAttestationSid,
WinAuthenticationFreshKeyAuthSid,
WinBuiltinDeviceOwnersSid
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool CreateWellKnownSid(WELL_KNOWN_SID_TYPE WellKnownSidType, IntPtr DomainSid, IntPtr pSid, ref uint cbSid);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment,
string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, out PROCESSINFO lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UpdateProcThreadAttribute(IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, [MarshalAs(UnmanagedType.Struct), In] ref SECURITY_CAPABILITIES caps, IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern void DeleteProcThreadAttributeList(IntPtr lpAttributeList);
Wow, first off, this is some great work and I'm curious how you are leveraging it (CICD? Security?). As a challenge I played around with the code and noticed that your declaration for the SECURITY_CAPABILITIES structure wasn't matching with the Microsoft documentation. They list the Capabilities member as a pointer to the SID_AND_ATTRIBUTES structure, not a structure itself. So, I changed that to IntPtr making it...
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_CAPABILITIES
{
public IntPtr AppContainerSid;
public IntPtr Capabilities;
public uint CapabilityCount;
public uint Reserved;
}
I'm not sure how to marshal an array of structs to an IntPtr so for now my example below is the single capability you listed.
//create one capability
SID_AND_ATTRIBUTES sidAndAttributes = new SID_AND_ATTRIBUTES();
sidAndAttributes.Sid = pSidInternetClientServer;
sidAndAttributes.Attributes = SE_GROUP_ENABLED;
IntPtr sidAndAttributesPtr = Marshal.AllocHGlobal(Marshal.SizeOf(sidAndAttributes));
Marshal.StructureToPtr(sidAndAttributes, sidAndAttributesPtr, false);
sc.Capabilities = sidAndAttributesPtr;
sc.CapabilityCount = 1;
This allowed notepad.exe to launch with AppContainer integrity and containing the capability flag you specified.
Thank you for the reply! I did get this to work. Your suggestion did help. This does actually launch a notepad.exe in an app container with two capabilities. Here is the code for the marshal for all that want to try this. My objective is to containerize some programs that we are using to protect the servers as much as possible.
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_CAPABILITIES
{
public IntPtr AppContainerSid;
public IntPtr Capabilities;
public int CapabilityCount;
public int Reserved;
}
private void AddDefaultCapabilitiesSid(ref SECURITY_CAPABILITIES sc)
{
//get the size, then the sid
IntPtr mem = IntPtr.Zero;
IntPtr clientServerSid = IntPtr.Zero;
IntPtr privateNetworkSid = IntPtr.Zero;
IntPtr pSid = IntPtr.Zero;
uint cbSid = 0;
//get the size, then the sid
SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, pSid, clientServerSid, ref cbSid);
clientServerSid = Marshal.AllocCoTaskMem(Convert.ToInt32(cbSid));
if (!SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, pSid, clientServerSid, ref cbSid))
{
throw new ApplicationException($"Unable to create well known Client Server capability sid!");
}
//get the size, then the sid
SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityPrivateNetworkClientServerSid, pSid, privateNetworkSid, ref cbSid);
privateNetworkSid = Marshal.AllocCoTaskMem(Convert.ToInt32(cbSid));
if (!SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityPrivateNetworkClientServerSid, pSid, privateNetworkSid, ref cbSid))
{
throw new ApplicationException($"Unable to create well known Client Server capability sid!");
}
SecurityNative.SID_AND_ATTRIBUTES[] sidAndAttributes = new SecurityNative.SID_AND_ATTRIBUTES[2];
sidAndAttributes[0].Sid = clientServerSid;
sidAndAttributes[0].Attributes = SE_GROUP_ENABLED;
sidAndAttributes[1].Sid = privateNetworkSid;
sidAndAttributes[1].Attributes = SE_GROUP_ENABLED;
int arraySize = sidAndAttributes.Length;
int elemSize = Marshal.SizeOf(typeof(SecurityNative.SID_AND_ATTRIBUTES));
IntPtr result = Marshal.AllocHGlobal(elemSize * arraySize);
mem = new IntPtr(result.ToInt64());
for (int i = 0; i < arraySize; i++)
{
IntPtr ptr = new IntPtr(result.ToInt64() + elemSize * i);
Marshal.StructureToPtr(sidAndAttributes[i], ptr, false);
MemToFree.Add(ptr); //free this mem later
}
sc.Capabilities = mem;
sc.CapabilityCount = arraySize;
}
I'd like to list out all CSP and CNG providers installed on a system, but I can't find a good method for doing so in C#. For CSP, I can enumerate a certain registry key (inelegant, but functional), but I've been unable to find any way to get a list of CNG providers.
Is there anything remotely like System.Security.Cryptography.Get[CSP/CNG]Providers() or similarly logical/straightforward in .NET that could be used? Thanks!
To my knowledge, there isn't anything like that in .NET Framework.
For CSP providers, enumerate the subkeys of:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider
For CNG providers, enumerate the subkeys of:
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Cryptography\Providers
Use this to enumerate CSP providers and containers:
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
namespace CspSample
{
struct Provider
{
public string Name { get; set; }
public int Type { get; set; }
}
class CspUtils
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CryptEnumProviderTypes(
uint dwIndex,
uint pdwReserved,
uint dwFlags,
[In] ref uint pdwProvType,
StringBuilder pszTypeName,
[In] ref uint pcbTypeName);
[DllImport("Advapi32.dll")]
private static extern bool CryptEnumProviders(
int dwIndex,
IntPtr pdwReserved,
int dwFlags,
ref int pdwProvType,
StringBuilder pszProvName,
ref int pcbProvName);
public static List<Provider> ListAllProviders()
{
List<Provider> installedCSPs = new List<Provider>();
int cbName;
int dwType;
int dwIndex;
StringBuilder pszName;
dwIndex = 0;
dwType = 1;
cbName = 0;
while (CryptEnumProviders(dwIndex, IntPtr.Zero, 0, ref dwType, null, ref cbName))
{
pszName = new StringBuilder(cbName);
if (CryptEnumProviders(dwIndex++, IntPtr.Zero, 0, ref dwType, pszName, ref cbName))
{
installedCSPs.Add(new Provider { Name = pszName.ToString(), Type = dwType });
}
}
return installedCSPs;
}
const int PP_ENUMCONTAINERS = 2;
const int PROV_RSA_FULL = 1;
const int ERROR_MORE_DATA = 234;
const int ERROR_NO_MORE_ITEMS = 259;
const int CRYPT_FIRST = 1;
const int CRYPT_NEXT = 2;
//TODO: Find how to disable this flag (not machine keystore)
const int CRYPT_MACHINE_KEYSET = 0x20;
const int CRYPT_VERIFYCONTEXT = unchecked((int)0xF0000000);
public static IList<string> EnumerateKeyContainers(string providerName, int providerType)
{
ProvHandle prov;
if (!CryptAcquireContext(out prov, null, providerName, providerType, CRYPT_MACHINE_KEYSET | CRYPT_VERIFYCONTEXT))
throw new Win32Exception(Marshal.GetLastWin32Error());
List<string> list = new List<string>();
IntPtr data = IntPtr.Zero;
try
{
int flag = CRYPT_FIRST;
int len = 0;
if (!CryptGetProvParam(prov, PP_ENUMCONTAINERS, IntPtr.Zero, ref len, flag))
{
if (Marshal.GetLastWin32Error() != ERROR_MORE_DATA)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
data = Marshal.AllocHGlobal(len);
do
{
if (!CryptGetProvParam(prov, PP_ENUMCONTAINERS, data, ref len, flag))
{
if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
break;
//throw new Win32Exception(Marshal.GetLastWin32Error());
}
list.Add(Marshal.PtrToStringAnsi(data));
flag = CRYPT_NEXT;
}
while (true);
}
finally
{
if (data != IntPtr.Zero)
{
Marshal.FreeHGlobal(data);
}
prov.Dispose();
}
return list;
}
private sealed class ProvHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public ProvHandle()
: base(true)
{
}
protected override bool ReleaseHandle()
{
return CryptReleaseContext(handle, 0);
}
[DllImport("advapi32.dll")]
private static extern bool CryptReleaseContext(IntPtr hProv, int dwFlags);
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool CryptAcquireContext(out ProvHandle phProv, string pszContainer, string pszProvider, int dwProvType, int dwFlags);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool CryptGetProvParam(ProvHandle hProv, int dwParam, IntPtr pbData, ref int pdwDataLen, int dwFlags);
}
}
I want to intercept drag&drop action on WebBrowser control. To be more precise, I want to be able to cancel or approve drag&drop event based on file extension (and process that file outside WebBrowser) and I want to make drag&drop icon look like regular icon: http://snag.gy/DUjMc.jpg, not like this one: http://snag.gy/ExX19.jpg.
I believe for this I need to implement custom IDocHostUIHandler and intercept GetDropTarget. Unfortunately, I have no success in doing that. I am using this code as my 'base' code: https://stackoverflow.com/a/19739699/2758677 and this part, that I made: http://pastebin.com/Ux947Eck. GetDropTarget is never called.
Here's a complete WinForms-based example that works, IDocHostUIHandler.GetDropTarget does get called.
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace CustomWebBrowser
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
var wb = new ImprovedWebBrowser();
wb.Dock = DockStyle.Fill;
this.Controls.Add(wb);
wb.Visible = true;
wb.DocumentText = "<body contentEditable='true'><b>Hello from ImprovedWebBrowser!</b></body>";
}
}
public class ImprovedWebBrowser : WebBrowser
{
/// <summary>
/// provide custom WebBrowserSite,
/// where we override IDocHostUIHandler and call the base implementation
/// More info: http://stackoverflow.com/a/19739699/1768303
/// </summary>
protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
{
return new ImprovedWebBrowserSite(this);
}
#region ImprovedWebBrowserSite
[ClassInterface(ClassInterfaceType.None)]
protected class ImprovedWebBrowserSite :
WebBrowserSite,
NativeMethods.IDocHostUIHandler,
IDisposable,
ICustomQueryInterface
{
ImprovedWebBrowser _host;
NativeMethods.IDocHostUIHandler _baseIDocHostUIHandler;
IntPtr _unkInnerAggregated;
IntPtr _unkOuter;
Inner _inner;
// constructor
public ImprovedWebBrowserSite(WebBrowser host) :
base(host)
{
_host = (ImprovedWebBrowser)host;
// get the CCW object for this
_unkOuter = Marshal.GetIUnknownForObject(this);
Marshal.AddRef(_unkOuter);
try
{
// aggregate the CCW object with the helper Inner object
_inner = new Inner(this);
_unkInnerAggregated = Marshal.CreateAggregatedObject(_unkOuter, _inner);
// obtain private WebBrowserSite COM interfaces
_baseIDocHostUIHandler = (NativeMethods.IDocHostUIHandler)Marshal.GetTypedObjectForIUnknown(_unkInnerAggregated, typeof(NativeMethods.IDocHostUIHandler));
}
finally
{
Marshal.Release(_unkOuter);
}
}
~ImprovedWebBrowserSite()
{
// need to work out the reference counting for GC to work correctly
Debug.Print("ImprovedWebBrowserSite object finalized.");
}
void IDisposable.Dispose()
{
base.Dispose();
_baseIDocHostUIHandler = null;
if (_unkInnerAggregated != IntPtr.Zero)
{
Marshal.Release(_unkInnerAggregated);
_unkInnerAggregated = IntPtr.Zero;
}
if (_unkOuter != IntPtr.Zero)
{
Marshal.Release(_unkOuter);
_unkOuter = IntPtr.Zero;
}
}
#region Inner
// Inner as aggregated object
class Inner :
ICustomQueryInterface,
IDisposable
{
object _outer;
Type[] _interfaces;
public Inner(object outer)
{
_outer = outer;
_interfaces = _outer.GetType().BaseType.GetInterfaces();
}
public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
{
if (_outer != null)
{
var guid = iid;
var iface = _interfaces.FirstOrDefault((t) => t.GUID == guid);
if (iface != null)
{
var unk = Marshal.GetComInterfaceForObject(_outer, iface, CustomQueryInterfaceMode.Ignore);
if (unk != IntPtr.Zero)
{
ppv = unk;
return CustomQueryInterfaceResult.Handled;
}
}
}
ppv = IntPtr.Zero;
return CustomQueryInterfaceResult.Failed;
}
~Inner()
{
// need to work out the reference counting for GC to work correctly
Debug.Print("Inner object finalized.");
}
public void Dispose()
{
_outer = null;
_interfaces = null;
}
}
#endregion
#region ICustomQueryInterface
public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
{
// CustomQueryInterfaceMode.Ignore is to avoid infinite loop during QI.
if (iid == typeof(NativeMethods.IDocHostUIHandler).GUID)
{
ppv = Marshal.GetComInterfaceForObject(this, typeof(NativeMethods.IDocHostUIHandler), CustomQueryInterfaceMode.Ignore);
}
else
{
ppv = IntPtr.Zero;
return CustomQueryInterfaceResult.NotHandled;
}
return CustomQueryInterfaceResult.Handled;
}
#endregion
#region IDocHostUIHandler
int NativeMethods.IDocHostUIHandler.ShowContextMenu(int dwID, ref NativeMethods.POINT pt, IntPtr pcmdtReserved, IntPtr pdispReserved)
{
return _baseIDocHostUIHandler.ShowContextMenu(dwID, ref pt, pcmdtReserved, pdispReserved);
}
int NativeMethods.IDocHostUIHandler.GetHostInfo(ref NativeMethods.DOCHOSTUIINFO info)
{
Debug.Print("IDocHostUIHandler.GetHostInfo");
return _baseIDocHostUIHandler.GetHostInfo(ref info);
}
int NativeMethods.IDocHostUIHandler.ShowUI(int dwID, IntPtr activeObject, IntPtr commandTarget, IntPtr frame, IntPtr doc)
{
return _baseIDocHostUIHandler.ShowUI(dwID, activeObject, commandTarget, frame, doc);
}
int NativeMethods.IDocHostUIHandler.HideUI()
{
return _baseIDocHostUIHandler.HideUI();
}
int NativeMethods.IDocHostUIHandler.UpdateUI()
{
return _baseIDocHostUIHandler.UpdateUI();
}
int NativeMethods.IDocHostUIHandler.EnableModeless(bool fEnable)
{
return _baseIDocHostUIHandler.EnableModeless(fEnable);
}
int NativeMethods.IDocHostUIHandler.OnDocWindowActivate(bool fActivate)
{
return _baseIDocHostUIHandler.OnDocWindowActivate(fActivate);
}
int NativeMethods.IDocHostUIHandler.OnFrameWindowActivate(bool fActivate)
{
return _baseIDocHostUIHandler.OnFrameWindowActivate(fActivate);
}
int NativeMethods.IDocHostUIHandler.ResizeBorder(ref NativeMethods.COMRECT rect, IntPtr doc, bool fFrameWindow)
{
return _baseIDocHostUIHandler.ResizeBorder(ref rect, doc, fFrameWindow);
}
int NativeMethods.IDocHostUIHandler.TranslateAccelerator(ref NativeMethods.MSG msg, ref Guid group, int nCmdID)
{
return _baseIDocHostUIHandler.TranslateAccelerator(ref msg, ref group, nCmdID);
}
int NativeMethods.IDocHostUIHandler.GetOptionKeyPath(string[] pbstrKey, int dw)
{
return _baseIDocHostUIHandler.GetOptionKeyPath(pbstrKey, dw);
}
int NativeMethods.IDocHostUIHandler.GetDropTarget(IntPtr pDropTarget, out IntPtr ppDropTarget)
{
Debug.Print("IDocHostUIHandler.GetDropTarget");
return _baseIDocHostUIHandler.GetDropTarget(pDropTarget, out ppDropTarget);
}
int NativeMethods.IDocHostUIHandler.GetExternal(out object ppDispatch)
{
return _baseIDocHostUIHandler.GetExternal(out ppDispatch);
}
int NativeMethods.IDocHostUIHandler.TranslateUrl(int dwTranslate, string strURLIn, out string pstrURLOut)
{
return _baseIDocHostUIHandler.TranslateUrl(dwTranslate, strURLIn, out pstrURLOut);
}
int NativeMethods.IDocHostUIHandler.FilterDataObject(IntPtr pDO, out IntPtr ppDORet)
{
return _baseIDocHostUIHandler.FilterDataObject(pDO, out ppDORet);
}
#endregion
}
#endregion
}
public static class NativeMethods
{
#region IDocHostUIHandler
public enum DOCHOSTUIDBLCLICK
{
DEFAULT = 0x0,
SHOWPROPERTIES = 0x1,
SHOWCODE = 0x2
}
public enum DOCHOSTUIFLAG
{
DIALOG = 0x1,
DISABLE_HELP_MENU = 0x2,
NO3DBORDER = 0x4,
SCROLL_NO = 0x8,
DISABLE_SCRIPT_INACTIVE = 0x10,
OPENNEWWIN = 0x20,
DISABLE_OFFSCREEN = 0x40,
FLAT_SCROLLBAR = 0x80,
DIV_BLOCKDEFAULT = 0x100,
ACTIVATE_CLIENTHIT_ONLY = 0x200,
NO3DOUTERBORDER = 0x00200000,
THEME = 0x00040000,
NOTHEME = 0x80000,
DISABLE_COOKIE = 0x400
}
[StructLayout(LayoutKind.Sequential)]
public struct DOCHOSTUIINFO
{
[MarshalAs(UnmanagedType.U4)]
public int cbSize;
[MarshalAs(UnmanagedType.I4)]
public int dwFlags;
[MarshalAs(UnmanagedType.I4)]
public int dwDoubleClick;
[MarshalAs(UnmanagedType.I4)]
public int dwReserved1;
[MarshalAs(UnmanagedType.I4)]
public int dwReserved2;
}
[StructLayout(LayoutKind.Sequential)]
public struct COMRECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
public IntPtr hwnd;
public int message;
public IntPtr wParam;
public IntPtr lParam;
public int time;
POINT pt;
}
[ComImport(), Guid("BD3F23C0-D43E-11CF-893B-00AA00BDCE1A"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDocHostUIHandler
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int ShowContextMenu(
[In, MarshalAs(UnmanagedType.U4)]
int dwID,
[In]
ref POINT pt,
[In]
IntPtr pcmdtReserved,
[In]
IntPtr pdispReserved);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetHostInfo(
[In, Out]
ref DOCHOSTUIINFO info);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int ShowUI(
[In, MarshalAs(UnmanagedType.I4)]
int dwID,
[In]
IntPtr activeObject,
[In]
IntPtr commandTarget,
[In]
IntPtr frame,
[In]
IntPtr doc);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int HideUI();
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int UpdateUI();
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int EnableModeless(
[In, MarshalAs(UnmanagedType.Bool)]
bool fEnable);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int OnDocWindowActivate(
[In, MarshalAs(UnmanagedType.Bool)]
bool fActivate);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int OnFrameWindowActivate(
[In, MarshalAs(UnmanagedType.Bool)]
bool fActivate);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int ResizeBorder(
[In]
ref COMRECT rect,
[In]
IntPtr doc,
bool fFrameWindow);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int TranslateAccelerator(
[In]
ref MSG msg,
[In]
ref Guid group,
[In, MarshalAs(UnmanagedType.I4)]
int nCmdID);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetOptionKeyPath(
[Out, MarshalAs(UnmanagedType.LPArray)]
String[] pbstrKey,
[In, MarshalAs(UnmanagedType.U4)]
int dw);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetDropTarget(
[In]
IntPtr pDropTarget,
[Out]
out IntPtr ppDropTarget);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetExternal(
[Out, MarshalAs(UnmanagedType.IDispatch)]
out object ppDispatch);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int TranslateUrl(
[In, MarshalAs(UnmanagedType.U4)]
int dwTranslate,
[In, MarshalAs(UnmanagedType.LPWStr)]
string strURLIn,
[Out, MarshalAs(UnmanagedType.LPWStr)]
out string pstrURLOut);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int FilterDataObject(
[In]
IntPtr pDO,
[Out]
out IntPtr ppDORet);
}
#endregion
}
}
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.