Get lpbaseaddress of a suspended process - winapi

Hi i have a source that does the following.
int[] context = new int[179];
context[0] = 65538; //context integer
GetThreadContext(PI.hThread, context); //from kernel32
ReadProcessMemory(PI.hProcess, context[41]+ 8, ref BaseAddress, 4, ref ReadWrite)
After googling much, context[41] refers to EBX. Any idea why? PInvokes.net shows the following.
[StructLayout(LayoutKind.Sequential)]
public struct CONTEXT
{
public uint ContextFlags; //set this to an appropriate value
// Retrieved by CONTEXT_DEBUG_REGISTERS
public uint Dr0;
public uint Dr1;
public uint Dr2;
public uint Dr3;
public uint Dr6;
public uint Dr7;
// Retrieved by CONTEXT_FLOATING_POINT
public FLOATING_SAVE_AREA FloatSave;
// Retrieved by CONTEXT_SEGMENTS
public uint SegGs;
public uint SegFs;
public uint SegEs;
public uint SegDs;
// Retrieved by CONTEXT_INTEGER
public uint Edi;
public uint Esi;
public uint Ebx;
public uint Edx;
public uint Ecx;
public uint Eax;
// Retrieved by CONTEXT_CONTROL
public uint Ebp;
public uint Eip;
public uint SegCs;
public uint EFlags;
public uint Esp;
public uint SegSs;
// Retrieved by CONTEXT_EXTENDED_REGISTERS
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
public byte[] ExtendedRegisters;
}
Also why must we ebx+8 to get the lpbaseaddress?

The CONTEXT structure is defined in winnt.h. Beware that it has different definitions based on the processor architecture. Use this structure definition to access the ebx register instead of a specific offset to the start. The EBX register points to the process's PEB (Process Environment Block) where the Ldr pointer contains the base address. All of this is used for a technique called 'Dynamic Forking' to run a process in the context of another process. Used for example in malware applications.

Related

How to access *.lnk files, (shortcuts) from a UWP app

I would like to be able to access *.lnk files from any folder on my desktop.
I know that Windows.Storage cannot access *.lnk files but I can read and access them from from Win32 or System.IO in WPF apps.
Is there a way for me to incorporate FindFirstFileEx() into my app without getting access permission errors?
I have seen articles on FullTrustProcessLauncher Class and CustomCapabilities settings but no examples using Kernal32 methods, or advice on even if this would work.
but no examples using Kernal32 methods
For this scenario, you could use desktop-bridge desktop extension to approach, for detail steps please refer stfan's blog UWP with Desktop Extension.
For using Kernal32 methods, please refer pinvoke document.
public enum FINDEX_INFO_LEVELS
{
FindExInfoStandard = 0,
FindExInfoBasic = 1
}
public enum FINDEX_SEARCH_OPS
{
FindExSearchNameMatch = 0,
FindExSearchLimitToDirectories = 1,
FindExSearchLimitToDevices = 2
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr FindFirstFileEx(
string lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
out WIN32_FIND_DATA lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
IntPtr lpSearchFilter,
int dwAdditionalFlags);

Windows Filtering Platform - add own layer c#

I am trying to hook outgoing connections with c#. I found this question:
Windows Filtering Platform - How can I block incoming connections based on local port?
But at sample some error with code and some missing classes to use.
I am trying to generate structs with p/invoke signature toolkit, but I have some very big class-generated code, where 90% calling dependence.
So I think I must realise external prototypes and then call them.
Here is my code:
public static class WpfProvider
{
private static void Test()
{
var RemotePort = 8080; //port to block
// connect to engine
var session = new FWPM_SESSION0_();
session.flags = 0xFFF;
IntPtr engineHandle;
FwpmEngineOpen0(null, RPC.RPC_C_AUTHN_WINNT, IntPtr.Zero, session, ref engineHandle);
// create a subLayer to attach filters to
var subLayerGuid = Guid.NewGuid();
var subLayer = new FWPM_SUBLAYER0_();
subLayer.subLayerKey = subLayerGuid;
subLayer.displayData.name = DisplayName;
subLayer.displayData.description = DisplayName;
subLayer.flags = 0;
subLayer.weight = 0x100;
FwpmSubLayerAdd0(engineHandle, subLayer, IntPtr.Zero);
var condition = new FWPM_FILTER_CONDITION0
{
fieldKey = Fwpm.FWPM_CONDITION_IP_REMOTE_PORT,
matchType = Fwpm.FWP_MATCH_TYPE.FWP_MATCH_EQUAL,
conditionValue =
{
type = Fwpm.FWP_DATA_TYPE.FWP_UINT16,
uint16 = RemotePort
}
};
// create the filter itself
var fwpFilter = new FWPM_FILTER0();
fwpFilter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
fwpFilter.action.type = FWP_ACTION_BLOCK;
fwpFilter.subLayerKey = subLayerGuid;
fwpFilter.weight.type = Fwpm.FWP_DATA_TYPE.FWP_EMPTY; // auto-weight.
fwpFilter.numFilterConditions = (uint)1;
var condsArray = new[] { condition };
var condsPtr = SafeNativeMethods.MarshalArray(condsArray); // helper to create a native array from a C# one
fwpFilter.filterCondition = condsPtr;
fwpFilter.displayData.name = DisplayName;
fwpFilter.displayData.description = DisplayName;
Microsoft.Win32.UnsaveNativeMethods
// add the filter
UInt64 filterId = 0L;
FwpmFilterAdd0(engineHandle, ref fwpFilter, IntPtr.Zero, out filterId));
}
/// Return Type: DWORD->unsigned int
///serverName: wchar_t*
///authnService: UINT32->unsigned int
///authIdentity: SEC_WINNT_AUTH_IDENTITY_W*
///session: FWPM_SESSION0*
///engineHandle: HANDLE*
[System.Runtime.InteropServices.DllImportAttribute("FWPUClnt.dll", EntryPoint = "FwpmEngineOpen0")]
public static extern uint FwpmEngineOpen0([System.Runtime.InteropServices.In()] [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string serverName, uint authnService, [System.Runtime.InteropServices.In()] IntPtr authIdentity, [System.Runtime.InteropServices.In()] IntPtr session, ref IntPtr engineHandle);
/// Return Type: DWORD->unsigned int
///engineHandle: HANDLE->void*
///subLayer: FWPM_SUBLAYER0*
///sd: PSECURITY_DESCRIPTOR->PVOID->void*
[System.Runtime.InteropServices.DllImportAttribute("FWPUClnt.dll", EntryPoint = "FwpmSubLayerAdd0")]
public static extern uint FwpmSubLayerAdd0([System.Runtime.InteropServices.In()] IntPtr engineHandle, [System.Runtime.InteropServices.In()] ref FWPM_SUBLAYER0 subLayer, [System.Runtime.InteropServices.In()] IntPtr sd);
/// Return Type: DWORD->unsigned int
///engineHandle: HANDLE->void*
///filter: FWPM_FILTER0*
///sd: PSECURITY_DESCRIPTOR->PVOID->void*
///id: UINT64*->UInt64
[System.Runtime.InteropServices.DllImportAttribute("FWPUClnt.dll", EntryPoint = "FwpmFilterAdd0")]
public static extern uint FwpmFilterAdd0([System.Runtime.InteropServices.In()] IntPtr engineHandle, [System.Runtime.InteropServices.In()] ref FWPM_FILTER0 filter, [System.Runtime.InteropServices.In()] IntPtr sd, UInt64 id);
}
static class RPC
{
public static uint RPC_C_AUTHN_NONE = 0;//No authentication.
public static uint RPC_C_AUTHN_DCE_PRIVATE = 1;//DCE private key authentication.
public static uint RPC_C_AUTHN_DCE_PUBLIC = 2;//DCE public key authentication.
public static uint RPC_C_AUTHN_DEC_PUBLIC = 4;//DEC public key authentication.Reserved for future use.
public static uint RPC_C_AUTHN_GSS_NEGOTIATE = 9;//Snego security support provider.
public static uint RPC_C_AUTHN_WINNT = 10;//NTLMSSP
public static uint RPC_C_AUTHN_GSS_SCHANNEL = 14;//Schannel security support provider. This authentication service supports SSL 2.0, SSL 3.0, TLS, and PCT.
public static uint RPC_C_AUTHN_GSS_KERBEROS = 16;//Kerberos security support provider.
public static uint RPC_C_AUTHN_DPA = 17;//DPA security support provider.
public static uint RPC_C_AUTHN_MSN = 18;//MSN security support provider.
public static uint RPC_C_AUTHN_KERNEL = 20;//Kernel security support provider.
public static uint RPC_C_AUTHN_DIGEST = 21;//Digest security support provider.
public static uint RPC_C_AUTHN_NEGO_EXTENDER = 30;//NEGO extender security support provider.
public static uint RPC_C_AUTHN_PKU2U = 31;//PKU2U security support provider.
public static uint RPC_C_AUTHN_MQ = 100;//MQ security support provider.
public static uint RPC_C_AUTHN_DEFAULT = 0xFFFFFFFF; //The system default authentication service. When this value is specified, COM uses its normal security blanket negotiation algorithm to pick an authentication service.For more information, see Security Blanket Negotiation.
}
#region WPF imports
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
public struct GUID
{
public uint Data1;/// unsigned int
public ushort Data2;/// unsigned short
public ushort Data3;/// unsigned short
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 8)]
public string Data4;// unsigned char[8]
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SID_IDENTIFIER_AUTHORITY
{
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = System.Runtime.InteropServices.UnmanagedType.I1)]
public byte[] Value;// BYTE[6]
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SID
{
public byte Revision;/// BYTE->unsigned char
public byte SubAuthorityCount;/// BYTE->unsigned char
public SID_IDENTIFIER_AUTHORITY IdentifierAuthority;/// SID_IDENTIFIER_AUTHORITY->_SID_IDENTIFIER_AUTHORITY
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = System.Runtime.InteropServices.UnmanagedType.U4)]
public uint[] SubAuthority;// DWORD[1]
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SEC_WINNT_AUTH_IDENTITY_W
{
public IntPtr User;/// unsigned short*
public uint UserLength;/// unsigned int
public IntPtr Domain;/// unsigned short*
public uint DomainLength;/// unsigned int
public IntPtr Password;/// unsigned short*
public uint PasswordLength;/// unsigned int
public uint Flags;// unsigned int
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct FWP_BYTE_BLOB
{
public uint size; /// UINT32->unsigned int
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]
public string data;// UINT8*
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct FWPM_DISPLAY_DATA0
{
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public string name;// wchar_t*
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public string description;// wchar_t*
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct FWPM_SESSION0
{
public GUID sessionKey;/// GUID->_GUID
public FWPM_DISPLAY_DATA0 displayData;/// FWPM_DISPLAY_DATA0->FWPM_DISPLAY_DATA0_
public uint flags;/// UINT32->unsigned int
public uint txnWaitTimeoutInMSec;/// UINT32->unsigned int
public uint processId;/// DWORD->unsigned int
public IntPtr sid;/// SID*
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public string username;/// wchar_t*
public int kernelMode;// BOOL->int
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct FWPM_SUBLAYER0
{
public GUID subLayerKey;/// GUID->_GUID
public FWPM_DISPLAY_DATA0 displayData;/// FWPM_DISPLAY_DATA0->FWPM_DISPLAY_DATA0_
public ushort flags;/// UINT16->unsigned short
public IntPtr providerKey;/// GUID*
public FWP_BYTE_BLOB providerData;/// FWP_BYTE_BLOB->FWP_BYTE_BLOB_
public ushort weight;// UINT16->unsigned short
}
Thanks

Hooking NtCreateFile API from ntdll.dll with EasyHook (c#)

This is the first time I try to hook windows API. My goal is to monitor all files that a process is going to create/open/read/write.
In order to be the most verbose possible, I decided to hook the ntdll.dll API such as NtCreateFile() and NtOpenFile(). So, in order to acheive this goal, I went on EasyHook, which seems easy and robust.
I've essetially followed the FileMon example, changing what I really wanted: the Hooked function.
When I try to read information about the file that is going to be opened, I try to read information from the OBJECT_ATTRIBUTES structure, such as
the ObjectName. Those are integer pointers, so I expected to use the function Marshal.PtrToStringAuto(attributes.objectName) in order to get the string value. However, the result is I can only have bad strings, without any meaning. Also, the File access seems to be not working. I guess there's something wrong with this
code, maybe in the DllImport signatures. Be adviced I had to replace SafeHandle with IntPtr, because of EasyHook was complaining about marshaling them.
Can someone help me?
Here's my specific code of the injected DLL:
Here's the Run method code
public void Run(RemoteHooking.IContext InContext, String inChannelName)
{
// First of all, install all the hooks
try
{
// NtCreateFile
fileCreationHook = LocalHook.Create(
LocalHook.GetProcAddress("ntdll.dll", "NtCreateFile"),
new CreateFileDelegate(CreateFile_Hooked),
this
);
fileCreationHook = LocalHook.Create(
LocalHook.GetProcAddress("ntdll.dll", "NtOpenFile"),
new OpenFileDelegate(OpenFile_Hooked),
this
);
fileCreationHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
remoteIf.Log("File creation Hook correctly installed on pid "+RemoteHooking.GetCurrentProcessId());
}
catch (Exception e)
{
remoteIf.Log(e.Message);
remoteIf.Log(e.StackTrace);
return;
}
// Wake up the process
remoteIf.Log("Waiking up process...");
RemoteHooking.WakeUpProcess();
while (true)
{
Thread.Sleep(500);
if (queue.Count > 0)
{
String[] package = null;
lock (queue)
{
package = queue.ToArray();
queue.Clear();
}
remoteIf.OnCreateFile(RemoteHooking.GetCurrentProcessId(), package);
}
else
remoteIf.Ping();
}
}
Here's the contructor code:
public InjectedDLL(RemoteHooking.IContext InContext, String inChannelName)
{
// Create the structure which will contain all the messages
queue = new Stack<string>();
// Initiate the connection to the Injector process, getting back its interface
remoteIf = RemoteHooking.IpcConnectClient<IPCInterface>(inChannelName);
// Try invocating a method to test the connection.
remoteIf.Ping();
}
Here there are the Hook delegate and the hook function
public delegate int CreateFileDelegate(out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
ref long allocSize,
uint fileAttributes,
System.IO.FileShare share,
uint createDisposition,
uint createOptions,
IntPtr eaBuffer,
uint eaLength);
public int CreateFile_Hooked(
out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
ref long allocSize,
uint fileAttributes,
System.IO.FileShare share,
uint createDisposition,
uint createOptions,
IntPtr eaBuffer,
uint eaLength)
{
//string s = Marshal.PtrToStringAuto(objectAttributes.ObjectName);
int res = NtCreateFile(out handle, access,ref objectAttributes,out ioStatus, ref allocSize,fileAttributes, share,createDisposition,createOptions,eaBuffer,eaLength);
return res;
}
Here there are the NtDll.Dll native functions:
[DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true)]
public static extern int NtCreateFile(
out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
ref long allocSize,
uint fileAttributes,
System.IO.FileShare share,
uint createDisposition,
uint createOptions,
IntPtr eaBuffer,
uint eaLength);
[DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true)]
public static extern int NtOpenFile(
out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
System.IO.FileShare share,
uint openOptions
);
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct OBJECT_ATTRIBUTES
{
public Int32 Length;
public IntPtr RootDirectory;
public IntPtr ObjectName;
public uint Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct IO_STATUS_BLOCK
{
public uint status;
public IntPtr information;
}
ObjectName is a pointer to a UNICODE_STRING struct. A managed equivalent looks like this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
[MarshalAs(UnmanagedType.LPWStr)]
public String Buffer;
}
You need to use marshalling to get a managed copy of the struct.
var us = Marshal.PtrToStructure<UNICODE_STRING>(objectAttributes.ObjectName);
Once you have the managed struct you can access the Buffer field to get the name of the object.

Creating a new dekstop using the Win32 API in Windows 8

I'm trying to write an application which will creates a new desktop using the Win32 API.
The below code works in Windows 7 but but in Windows 8 the desktop is created with out the desktop icons and with an empty taskbar.
public class Desktop
{
private const int NORMAL_PRIORITY_CLASS = 0x00000020;
private const long DF_ALLOWOTHERACCOUNTHOOK = 0x0001L;
private const uint DELETE = 0x00010000;
private const uint READ_CONTROL = 0x00020000;
private const uint WRITE_DAC = 0x00040000;
private const uint WRITE_OWNER = 0x00080000;
private const uint STANDARD_RIGHTS_REQUIRED = DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER;
private const long DESKTOP_CREATEWINDOW = 0x0002L;
private const long DESKTOP_ENUMERATE = 0x0040L;
private const long DESKTOP_WRITEOBJECTS = 0x0080L;
private const long DESKTOP_SWITCHDESKTOP = 0x0100L;
private const long DESKTOP_CREATEMENU = 0x0004L;
private const long DESKTOP_HOOKCONTROL = 0x0008L;
private const long DESKTOP_READOBJECTS = 0x0001L;
private const long DESKTOP_JOURNALRECORD = 0x0010L;
private const long DESKTOP_JOURNALPLAYBACK = 0x0020L;
private const uint AccessRights = (uint)DESKTOP_JOURNALRECORD |
(uint)DESKTOP_JOURNALPLAYBACK |
(uint)DESKTOP_CREATEWINDOW |
(uint)DESKTOP_ENUMERATE |
(uint)DESKTOP_WRITEOBJECTS |
(uint)DESKTOP_SWITCHDESKTOP |
(uint)DESKTOP_CREATEMENU |
(uint)DESKTOP_HOOKCONTROL |
(uint)DESKTOP_READOBJECTS |
STANDARD_RIGHTS_REQUIRED;
private delegate bool EnumDesktopProc(string lpszDesktop, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr GetProcessWindowStation();
[DllImport("user32.dll")]
private static extern bool EnumDesktops(IntPtr hwinsta, EnumDesktopProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr CreateDesktop(string desktopName, string device, string deviceMode, int flags, uint accessMask, [In] ref SECURITY_ATTRIBUTES attributes);
[DllImport("user32.dll")]
private static extern IntPtr OpenDesktop(string lpszDesktop, uint dwFlags, bool fInherit, uint dwDesiredAccess);
[DllImport("user32.dll")]
private static extern bool SwitchDesktop(IntPtr hDesktop);
[DllImport("kernel32.dll")]
private static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, ref PROCESS_INFORMATION lpProcessInformation);
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
private struct STARTUPINFO
{
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
private static List<string> _desktops = new List<string>();
public static IntPtr Create(string name)
{
var securityAttributes = new SECURITY_ATTRIBUTES();
securityAttributes.nLength = Marshal.SizeOf(securityAttributes);
return CreateDesktop(name, null, null, (int) DF_ALLOWOTHERACCOUNTHOOK, AccessRights, ref securityAttributes);
}
public static IntPtr Open(string name)
{
return OpenDesktop(name, 0, false, AccessRights);
}
public static bool Show(IntPtr desktopPrt)
{
if (desktopPrt == IntPtr.Zero) return false;
return SwitchDesktop(desktopPrt);
}
public static void Prepare(string desktopName)
{
CreateProcess("C:\\Windows\\explorer.exe", desktopName);
}
public static bool Exists(string name)
{
return GetDesktops().Any(s => s == name);
}
private static void CreateProcess(string path, string desktopName)
{
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = desktopName;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
var lpCurrentDirectory = Path.GetDirectoryName(path);
CreateProcess(path, null, IntPtr.Zero, IntPtr.Zero, true, NORMAL_PRIORITY_CLASS, IntPtr.Zero, lpCurrentDirectory, ref si, ref pi);
}
private static string[] GetDesktops()
{
IntPtr windowStation = GetProcessWindowStation();
if (windowStation == IntPtr.Zero) return new string[0];
_desktops.Clear();
bool result = EnumDesktops(windowStation, DesktopEnumProc, IntPtr.Zero);
if (!result) return new string[0];
return _desktops.ToArray();
}
private static bool DesktopEnumProc(string lpszDesktop, IntPtr lParam)
{
_desktops.Add(lpszDesktop);
return true;
}
}
Usage:
var desktopName = "TestDesktop";
var desktopPrt = Desktop.Create(desktopName);
Desktop.Show(desktopPrt);
Desktop.Prepare(desktopName);
The Sysinternals Desktops app (http://technet.microsoft.com/en-us/sysinternals/cc817881.aspx) uses the same API and it works in Windows 8 so there must be something that I am missing.
Does anyone have any pointers for me? Or does anyone know of any where I can find examples of how to use this API? MSDN for the API calls gives a lot of information but no many examples.
Thanks ;D
EDIT: Sry, I realised that you run it in your code snippet, please ignore my comment.
How about explicitly running explorer.exe for each desktop you create? That one handles icons, taskbar, etc ...
When you run SysInternals Desktops app you can see that it actually does that.

Calling SHGetFileInfo in thread to avoid UI freeze

In a .NET 4.0 application (WPF) we're using SHGetFileInfo to obtain shell icons for a directory tree. Since this takes quite some time in some cases (i.e. for a network drive that is unreachable or for a floppy drive), we wanted to do this in a thread and then update the icon when it has been read in.
The call is basically the same, it is now just executed within a thread. Because someone said that the thread must be STA for this to work, we used Thread rather than ThreadPool for testing, with the same results. Using ThreadPool also did not work.
SHGetFileInfo succeeds (returns 1), but the hIcon member in the structure is zero.
IntPtr GetIcon(string name)
{
Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES | Shell32.SHGFI_SMALLICON;
Shell32.SHGetFileInfo(
name, System.IO.Directory.Exists(name) ? Shell32.FILE_ATTRIBUTE_DIRECTORY : Shell32.FILE_ATTRIBUTE_NORMAL,
ref shfi,
(uint) System.Runtime.InteropServices.Marshal.SizeOf(shfi),
flags );
return shfi.hIcon;
}
The very same code works fine from the GUI thread. What has to be done to make the function work from a separate thread, or, however, make it work without blocking the GUI thread?
Update: The code around this is basically this:
var thread = new System.Threading.Thread(() => {
var result = GetIcon("C:\\");
// ... do something with the result
});
thread.SetApartmentState(System.Threading.ApartmentState.STA);
thread.Start();
if only the lines within the thread delegate are left in, it works fine (but on the GUI thread, of course).
Update: For now, we just Invoke the call to SHGetFileInfo to make it work. This has the advantage that the original problem (the page with the file view was not displayed until all the icons have been loaded) has been solved, though it means that the page hangs for each icon. But at least the user now sees that something is going on. We're still looking for an actual solution to the problem.
Just successfully got something just like this working. It had previously been crashing badly when run outside of Visual Studio. Before that we'd often been getting back the default icon rather than the correct one for the file type (since we're after file icons, not the directory icon).
A summary of the factors to consider.
As discussed already, interacting with the Shell requires using a STA thread which pumps messages. A BackgroundWorker isn't going to be enough here.
When you initialize SHFILEINFO, set both string properties (display name and type name) to string.Empty. This isn't shown in most samples, but failing to do this was causing a crash for us. (In debug mode it meant that the first icon we got back was wrong, which could be the same as your problem.)
Check your interop declarations are right. For example, the SHFILEINFO class should presumably have a [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)] attribute.
A TaskScheduler For Background Tasks on an STA Thread
We're using the Task Parallel Library, so wanted a TaskScheduler that would schedule work on a suitable background thread. The following code sample is for a class that exposes a TaskScheduler property that can be used for this.
Note that in our we have a single instance of this class that lasts for the whole lifetime of the application, so we've not implemented IDisposable. If you want to create/destroy these you'll need to handle that.
namespace MyNamespace
{
using System;
using System.ComponentModel.Composition;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
/// <summary>
/// Exposes a <see cref="TaskScheduler"/> that schedules its work on a STA background thread.
/// </summary>
[Export]
public class StaTaskSchedulerSource
{
/// <summary>
/// A window that is used for message pumping.
/// </summary>
private Window window;
/// <summary>
/// Thread on which work is scheduled.
/// </summary>
private Thread thread;
/// <summary>
/// The <see cref="TaskScheduler"/> exposed by this class.
/// </summary>
private TaskScheduler taskScheduler;
/// <summary>
/// Initializes a new instance of the <see cref="StaTaskSchedulerSource"/> class.
/// </summary>
public StaTaskSchedulerSource()
{
using (ManualResetEvent re = new ManualResetEvent(false))
{
this.thread = new Thread(
() =>
{
this.window = new Window();
re.Set();
Dispatcher.Run();
});
this.thread.IsBackground = true;
this.thread.SetApartmentState(ApartmentState.STA);
this.thread.Start();
re.WaitOne();
}
this.window.Dispatcher.Invoke(
new Action(
() =>
{
this.taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}));
}
/// <summary>
/// Gets a <see cref="TaskScheduler"/> that schedules work on a background STA
/// thread.
/// </summary>
public TaskScheduler TaskScheduler
{
get
{
return this.taskScheduler;
}
}
}
}
Of course, this whole thing just wraps using a dispatcher to invoke the calls on another thread. This can be a bit slow. So if processing many icons it would be better to batch them up. Also, we cache icons we've already retrieved so we don't need to use the Win Shell at all the second time around.
SafeIconHandle
You may also find the following wrapper for an icon handle useful. It derives from SafeHandle and ensures that you icon is properly destroyed under all circumstances.
namespace UnmanagedResourceLib
{
using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using Microsoft.Win32.SafeHandles;
/// <summary>
/// A <see cref="SafeHandle"/> implementation for HICON icon handles.
/// </summary>
[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
internal class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid
{
/// <summary>
/// Prevents a default instance of the <see cref="SafeIconHandle"/> class from being created.
/// </summary>
private SafeIconHandle()
: base(true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SafeIconHandle"/> class.
/// </summary>
/// <param name="nativeHandle">The HICON to wrap.</param>
/// <param name="ownsHandle"><c>true</c> if finalization of this object should cause the icon to be destroyed.</param>
public SafeIconHandle(IntPtr nativeHandle, bool ownsHandle)
: base(ownsHandle)
{
this.handle = nativeHandle;
}
/// <inheritdoc />
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
override protected bool ReleaseHandle()
{
return NativeMethods.DestroyIcon(this.handle);
}
/// <summary>
/// Exposes Windows API call to destroy an icon.
/// </summary>
[SuppressUnmanagedCodeSecurity]
internal static class NativeMethods
{
/// <summary>
/// Destroys an icon and frees any memory the icon occupied.
/// </summary>
/// <param name="hIcon">A handle to the icon to be destroyed.</param>
/// <returns><c>true</c> if the function succeeds.</returns>
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
public static extern bool DestroyIcon(IntPtr hIcon);
}
}
}
I don't think there's any problem. You don't need to use SetApartmentState. According to the documentation you do need to have called CoInitialize or OleInitialize, but I think this should have been called for you anyway by WPF.
I created a simple WPF application below. This works fine. SHGetFileInfo runs on a different thread to the UI thread and shfi.hIcon is not zero.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
Task<IntPtr> task = Task.Factory.StartNew(() => GetIcon("C:\\"));
}
private IntPtr GetIcon(string name)
{
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
var shfi = new Shell32.SHFILEINFO();
uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES | Shell32.SHGFI_SMALLICON;
Shell32.SHGetFileInfo(
name,
Directory.Exists(name) ? Shell32.FILE_ATTRIBUTE_DIRECTORY : Shell32.FILE_ATTRIBUTE_NORMAL,
ref shfi,
(uint) Marshal.SizeOf(shfi),
flags);
Debug.WriteLine(shfi.hIcon);
return shfi.hIcon;
}
}
public class Shell32
{
public const int MAX_PATH = 256;
// Browsing for directory.
public const uint BIF_RETURNONLYFSDIRS = 0x0001;
public const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
public const uint BIF_STATUSTEXT = 0x0004;
public const uint BIF_RETURNFSANCESTORS = 0x0008;
public const uint BIF_EDITBOX = 0x0010;
public const uint BIF_VALIDATE = 0x0020;
public const uint BIF_NEWDIALOGSTYLE = 0x0040;
public const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
public const uint BIF_BROWSEINCLUDEURLS = 0x0080;
public const uint BIF_BROWSEFORCOMPUTER = 0x1000;
public const uint BIF_BROWSEFORPRINTER = 0x2000;
public const uint BIF_BROWSEINCLUDEFILES = 0x4000;
public const uint BIF_SHAREABLE = 0x8000;
public const uint SHGFI_ICON = 0x000000100; // get icon
public const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name
public const uint SHGFI_TYPENAME = 0x000000400; // get type name
public const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes
public const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location
public const uint SHGFI_EXETYPE = 0x000002000; // return exe type
public const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index
public const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon
public const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state
public const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes
public const uint SHGFI_LARGEICON = 0x000000000; // get large icon
public const uint SHGFI_SMALLICON = 0x000000001; // get small icon
public const uint SHGFI_OPENICON = 0x000000002; // get open icon
public const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon
public const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl
public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute
public const uint SHGFI_ADDOVERLAYS = 0x000000020; // apply the appropriate overlays
public const uint SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
[DllImport("Shell32.dll")]
public static extern IntPtr SHGetFileInfo(
string pszPath,
uint dwFileAttributes,
ref SHFILEINFO psfi,
uint cbFileInfo,
uint uFlags
);
#region Nested type: BROWSEINFO
[StructLayout(LayoutKind.Sequential)]
public struct BROWSEINFO
{
public IntPtr hwndOwner;
public IntPtr pidlRoot;
public IntPtr pszDisplayName;
[MarshalAs(UnmanagedType.LPTStr)] public string lpszTitle;
public uint ulFlags;
public IntPtr lpfn;
public int lParam;
public IntPtr iImage;
}
#endregion
#region Nested type: ITEMIDLIST
[StructLayout(LayoutKind.Sequential)]
public struct ITEMIDLIST
{
public SHITEMID mkid;
}
#endregion
#region Nested type: SHFILEINFO
[StructLayout(LayoutKind.Sequential)]
public struct SHFILEINFO
{
public const int NAMESIZE = 80;
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)] public string szTypeName;
};
#endregion
#region Nested type: SHITEMID
[StructLayout(LayoutKind.Sequential)]
public struct SHITEMID
{
public ushort cb;
[MarshalAs(UnmanagedType.LPArray)] public byte[] abID;
}
#endregion
}
/// <summary>
/// Wraps necessary functions imported from User32.dll. Code courtesy of MSDN Cold Rooster Consulting example.
/// </summary>
public class User32
{
/// <summary>
/// Provides access to function required to delete handle. This method is used internally
/// and is not required to be called separately.
/// </summary>
/// <param name="hIcon">Pointer to icon handle.</param>
/// <returns>N/A</returns>
[DllImport("User32.dll")]
public static extern int DestroyIcon(IntPtr hIcon);
}

Resources