I'm trying to import and use CryptCATCDFEnumMembersByCDFTagEx in order to create .cat files using C#. The function is returning null and the parameters do not seem to be initialized. I wrote the corresponding code in C++ and that works fine. I'm guessing my structures are off.
Any help would be appreciated. The code should create a Good.cat file in the working directory of the program, which should contain the hashes of the files specified below [CatalogFiles].
Catalog.cdf
[CatalogHeader]
Name=Good.cat
ResultDir=.\
PublicVersion=0x0000001
EncodingType=0x00010001
CATATTR1=0x10010001:OSAttr:2:6.0
[CatalogFiles]
Notepad.exe=C:\Windows\Notepad.exe
C# sample program.
class Program
{
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb427419%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct CRYPTCATCDF
{
uint cbStruct;
IntPtr hFile;
uint dwCurFilePos;
uint dwLastMemberOffset;
bool fEOF;
[MarshalAs(UnmanagedType.LPWStr)]
string pwszResultDir;
IntPtr hCATStore;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa381414%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct CRYPTOAPI_BLOB
{
public uint cbData; // UInt32 cbData;
//[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
public IntPtr pbData; //public byte[] pbData
} // CRYPT_INTEGER_BLOB, CRYPT_ATTR_BLOB, CRYPT_OBJID_BLOB, CRYPT_HASH_BLOB
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb736433%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct SIP_INDIRECT_DATA
{
public CRYPT_ATTRIBUTE_TYPE_VALUE Data;
public CRYPT_ALGORITHM_IDENTIFIER DigestAlgorithm;
public CRYPTOAPI_BLOB Digest;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa381151%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_ATTRIBUTE_TYPE_VALUE {
[MarshalAs(UnmanagedType.LPStr)]
public string pszObjId;
public CRYPTOAPI_BLOB Value;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa381133%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_ALGORITHM_IDENTIFIER
{
[MarshalAs(UnmanagedType.LPStr)]
public string pszObjId;
public CRYPTOAPI_BLOB Parameters;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa373931%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct GUID
{
int a;
short b;
short c;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
byte[] d;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa379905%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public class CRYPTCATMEMBER
{
public uint cbStruct;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszReferenceTag;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszFileName;
public GUID gSubjectType;
public uint fdwMemberFlags;
public IntPtr pIndirectData; //struct SIP_INDIRECT_DATA_ *pIndirectData;
public uint dwCertVersion;
public uint dwReserved;
public IntPtr hReserved;
public CRYPTOAPI_BLOB sEncodedIndirectData;
public CRYPTOAPI_BLOB sEncodedMemberInfo;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb410248%28v=vs.85%29.aspx
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void PFN_CDF_PARSE_ERROR_CALLBACK(
[In] uint dwErrorArea,
[In] uint dwLocalError,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszLine
);
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb427424%28v=vs.85%29.aspx
[DllImport("wintrust.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr CryptCATCDFOpen(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilePath,
[In, Optional] IntPtr pfnParseError
);
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb427423%28v=vs.85%29.aspx
[DllImport("wintrust.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)]
static extern string CryptCATCDFEnumMembersByCDFTagEx(
[In] IntPtr pCDF,
[In, Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszPrevCDFTag,
[In] IntPtr pfnParseError,
[In] CRYPTCATMEMBER ppMember,
[In] bool fContinueOnError,
[In] IntPtr pvReserved
);
private static void ParseErrorCallback(uint u1, uint u2, string s)
{
Console.WriteLine(u1 + " " + u2 + " " + s);
}
static void Main(string[] args)
{
PFN_CDF_PARSE_ERROR_CALLBACK pfn = new PFN_CDF_PARSE_ERROR_CALLBACK(ParseErrorCallback);
StringBuilder sb = new StringBuilder(256);
string s = string.Empty;
IntPtr cdfPtr = CryptCATCDFOpen("catalog.cdf", Marshal.GetFunctionPointerForDelegate(pfn));
CRYPTCATCDF cdf = (CRYPTCATCDF)Marshal.PtrToStructure(cdfPtr, typeof(CRYPTCATCDF));
CRYPTCATMEMBER ccm = new CRYPTCATMEMBER();
ccm.pIndirectData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIP_INDIRECT_DATA)));
do
{
s = CryptCATCDFEnumMembersByCDFTagEx(cdfPtr, sb, Marshal.GetFunctionPointerForDelegate(pfn), ccm, true, IntPtr.Zero);
Console.WriteLine(s ?? "N/A");
} while (s != null);
}
}
Working C++ example
void callback(DWORD u1, DWORD u2, LPWSTR s)
{
printf("%d %d %s", u1, u2, s);
}
typedef LPWSTR(WINAPI *CryptCATCDFEnumMembersByCDFTagEx)(
CRYPTCATCDF *pCDF,
LPWSTR pwszPrevCDFTag,
PFN_CDF_PARSE_ERROR_CALLBACK pfnParseError,
CRYPTCATMEMBER **ppMember,
BOOL fContinueOnError,
LPVOID pvReserved);
int _tmain(int argc, _TCHAR* argv[])
{
CRYPTCATCDF *cdf;
CRYPTCATMEMBER *pMember = NULL;
LPWSTR pwszMemberTag = NULL;
HINSTANCE dllHandle = LoadLibrary(L"wintrust.dll");
cdf = CryptCATCDFOpen(L"catalog.cdf", (PFN_CDF_PARSE_ERROR_CALLBACK)callback);
CryptCATCDFEnumMembersByCDFTagEx fptr = (CryptCATCDFEnumMembersByCDFTagEx)GetProcAddress(dllHandle, "CryptCATCDFEnumMembersByCDFTagEx");
while (pwszMemberTag = (*fptr)(cdf, pwszMemberTag, NULL, &pMember, TRUE, NULL))
{
wprintf(L"%s", pwszMemberTag);
}
CryptCATCDFClose(cdf);
}
With minor changes to your program, I'm able to create the catalog file. Did not modify any structs or its members. Still using "CRYPTCATMEMBER" as class
Changes:
Assign null to string variable, in this case "s".
Calling CryptCATCDFClose function after enumerating the files in catalog definition file(.cdf).
Working Console program.
static void Main(string[] args)
{
CRYPTCATMEMBER ccm = null;
try
{
PFN_CDF_PARSE_ERROR_CALLBACK pfn = ParseErrorCallback;
string s = null; //This null assignment is deliberately done.
IntPtr cdfPtr = CryptCATCDFOpen("catalog.cdf", Marshal.GetFunctionPointerForDelegate(pfn));
CRYPTCATCDF cdf = (CRYPTCATCDF) Marshal.PtrToStructure(cdfPtr, typeof(CRYPTCATCDF)); //This call is required else the catlog file creation fails
ccm = new CRYPTCATMEMBER
{
pIndirectData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIP_INDIRECT_DATA)))
};
do
{
s = CryptCATCDFEnumMembersByCDFTagEx(cdfPtr, s, Marshal.GetFunctionPointerForDelegate(pfn), ccm, true, IntPtr.Zero);
Console.WriteLine(s ?? "N/A");
} while (s != null);
CryptCATCDFClose(cdfPtr); //This is required to update the .cat with the files details specified in .cdf file.
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
finally
{
// Free the unmanaged memory.
if (ccm != null)
{
Marshal.FreeHGlobal(ccm.pIndirectData);
}
}
}
Related
I'm trying to call into the crypt32.dll method CryptProtectData from managed code, but I do not seem to have the marshaling types quite right in my delegate's declaration:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool CryptProtectDataDelegate(
IntPtr pDataIn, // DATA_BLOB*
//StringBuilder szDataDescr, // LPCWSTR
[MarshalAs(UnmanagedType.LPWStr)] string szDataDescr, // LPCWSTR
IntPtr pOptionalEntropy, // DATA_BLOB*
int pvReserved, // PVOID
IntPtr pPromptStruct, // CRYPTPROTECT_PROMPTSTRUCT*
int dwFlags, // DWORD
IntPtr pDataOut // DATA_BLOB*
);
which when invoked,
bool theResult = cryptProtectData(
pDataIn,
null,
IntPtr.Zero, // null,
0, // null,
IntPtr.Zero, // null,
flag,
pDataOut);
causes the exception
+CryptProtectDataDelegate::Invoke' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the
unmanaged target signature. Check that the calling convention and
parameters of the PInvoke signature match the target unmanaged
signature.'
The native definition for CryptProtectData is
DPAPI_IMP BOOL CryptProtectData(
DATA_BLOB *pDataIn,
LPCWSTR szDataDescr,
DATA_BLOB *pOptionalEntropy,
PVOID pvReserved,
CRYPTPROTECT_PROMPTSTRUCT *pPromptStruct,
DWORD dwFlags,
DATA_BLOB *pDataOut
);
where DPAPI_IMP is defined as
#define DPAPI_IMP DECLSPEC_IMPORT
I'm note sure what the legal type representations are that I should be using for the parameters in the delegate definition?
Assuming PVOID is a void *, I found some documentation which suggests that it could be represented as an int, and b/c it can be null, I set it to 0 (https://learn.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-behavior).
Below is what should be a complete and run-able example (that will crash)
using System;
using System.Runtime.InteropServices;
namespace CallNativeDLLs
{
static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
class Program
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool CryptProtectDataDelegate(
IntPtr pDataIn, // DATA_BLOB*
//StringBuilder szDataDescr, // LPCWSTR
[MarshalAs(UnmanagedType.LPWStr)] string szDataDescr, // LPCWSTR
IntPtr pOptionalEntropy, // DATA_BLOB*
int pvReserved, // PVOID
IntPtr pPromptStruct, // CRYPTPROTECT_PROMPTSTRUCT*
int dwFlags, // DWORD
IntPtr pDataOut // DATA_BLOB*
);
static void Main(string[] args)
{
IntPtr pDll = NativeMethods.LoadLibrary(#"c:\windows\system32\crypt32.DLL");
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "CryptProtectData");
var cryptProtectData = (CryptProtectDataDelegate)Marshal.GetDelegateForFunctionPointer(
pAddressOfFunctionToCall,
typeof(CryptProtectDataDelegate));
IntPtr pDataIn = Marshal.StringToHGlobalAnsi("hi");
int flag = (int)0x4; //CRYPTPROTECT_LOCAL_MACHINE
var pDataOut = new IntPtr();
// EXCEPTION thrown here
bool theResult = cryptProtectData(
pDataIn,
null,
IntPtr.Zero, // null,
0, // null,
IntPtr.Zero, // null,
flag,
pDataOut);
bool result = NativeMethods.FreeLibrary(pDll);
Console.WriteLine(theResult);
}
}
}
Update: Using Amy's link, the following runs (though I haven't tried decrypting the string yet, and CRYPTPROTECT_LOCAL_MACHINE is weak)
using System;
using System.Runtime.InteropServices;
namespace CallNativeDLLs
{
static class NativeMethods
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DATA_BLOB
{
public int cbData;
public IntPtr pbData;
}
[Flags]
public enum CryptProtectPromptFlags
{
CRYPTPROTECT_PROMPT_ON_UNPROTECT = 0x1,
CRYPTPROTECT_PROMPT_ON_PROTECT = 0x2
}
[Flags]
public enum CryptProtectFlags
{
CRYPTPROTECT_UI_FORBIDDEN = 0x1,
CRYPTPROTECT_LOCAL_MACHINE = 0x4,
CRYPTPROTECT_CRED_SYNC = 0x8,
CRYPTPROTECT_AUDIT = 0x10,
CRYPTPROTECT_NO_RECOVERY = 0x20,
CRYPTPROTECT_VERIFY_PROTECTION = 0x40
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CRYPTPROTECT_PROMPTSTRUCT
{
public int cbSize;
public CryptProtectPromptFlags dwPromptFlags;
public IntPtr hwndApp;
public String szPrompt;
}
[DllImport("Crypt32.dll",
SetLastError = true,
CharSet = System.Runtime.InteropServices.CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptProtectData(
ref DATA_BLOB pDataIn,
String szDataDescr,
ref DATA_BLOB pOptionalEntropy,
IntPtr pvReserved,
ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct,
CryptProtectFlags dwFlags,
ref DATA_BLOB pDataOut
);
}
class Program
{
static void Main(string[] args)
{
IntPtr pbData = Marshal.StringToHGlobalAnsi("hi");
var pDataIn = new NativeMethods.DATA_BLOB{cbData=0,pbData=pbData};
var pOptionalEntropy = new NativeMethods.DATA_BLOB();
var pPromptStruct = new NativeMethods.CRYPTPROTECT_PROMPTSTRUCT();
var pDataOut = new NativeMethods.DATA_BLOB();
bool theResult = NativeMethods.CryptProtectData(
ref pDataIn,
null,
ref pOptionalEntropy, // null,
IntPtr.Zero, // null,
ref pPromptStruct, // null,
NativeMethods.CryptProtectFlags.CRYPTPROTECT_LOCAL_MACHINE,
ref pDataOut);
Console.WriteLine(theResult);
}
}
}
how to delete multi-items via C# or windows api, I have search for this for a long time and no solution. I used FolderItemVerb in Shell32.dll to delete file one by one, but it would pop a dialog at same time. I will be appreciated if you know how to solve this problem. Thank you.
It isn't for the faint of heart!
Code compatible with Windows >= Vista, no XP! The XP code is at the end
This is the first time I take a look at the Shell32 interfaces... Wow... Even the most simple things are complex :-) But then, COM is always complex... Now... There are 2.5 competing data structures/interfaces for manipulating files in Shell... IShellFolder (the old COM interface, not used in this sample), IShellItem (the new COM interface, used in this sample), IDLIST (here used as a PIDLIST_ABSOLUTE, used for "collecting" multiple files together. I count it as 0.5 😀). I hope there is no memory leak anywhere (always complex). Note that .NET will free COM objects automatically. I've even added some code that I've written to help me debug (like the code to get the list of verbs supported).
Now, here there are the COM interfaces plus some Shell32.dll PInvoke methods.
[StructLayout(LayoutKind.Sequential)]
public struct PIDLIST_ABSOLUTE
{
public IntPtr Ptr;
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
public interface IShellItem
{
void BindToHandler(
IntPtr pbc,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(int sigdnName, out IntPtr ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
[PreserveSig]
int Compare(IShellItem psi, uint hint, out int piOrder);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("70629033-e363-4a28-a567-0db78006e6d7")]
public interface IEnumShellItems
{
void Next(int celt, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] IShellItem[] rgelt, out int pceltFetched);
void Skip(int celt);
void Reset();
void Clone(out IEnumShellItems ppenum);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("b63ea76d-1f85-456f-a19c-48159efa858b")]
public interface IShellItemArray
{
void BindToHandler(
IntPtr pbc,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppvOut);
void GetPropertyStore(
uint flags,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void GetPropertyDescriptionList(
IntPtr keyType,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void GetAttributes(uint AttribFlags, uint sfgaoMask, out uint psfgaoAttribs);
void GetCount(out int pdwNumItems);
void GetItemAt(int dwIndex, out IShellItem ppsi);
void EnumItems(out IEnumShellItems ppenumShellItems);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214e4-0000-0000-c000-000000000046")]
public interface IContextMenu
{
[PreserveSig]
int QueryContextMenu(IntPtr hMenu, uint indexMenu, int idCmdFirst, int idCmdLast, uint uFlags);
void InvokeCommand([In] ref CMINVOKECOMMANDINFOEX pici);
[PreserveSig]
int GetCommandString(UIntPtr idCmd, uint uType, IntPtr pReserved, IntPtr pszName, int cchMax);
}
[StructLayout(LayoutKind.Sequential)]
public struct CMINVOKECOMMANDINFOEX
{
public int cbSize;
public uint fMask;
public IntPtr hwnd;
// Non-unicode verbs (are there unicode verbs?)
[MarshalAs(UnmanagedType.LPStr)]
public string lpVerb;
[MarshalAs(UnmanagedType.LPStr)]
public string lpParameters;
[MarshalAs(UnmanagedType.LPStr)]
public string lpDirectory;
public int nShow;
public uint dwHotKey;
public IntPtr hIcon;
[MarshalAs(UnmanagedType.LPStr)]
public string lpTitle;
// Use CMIC_MASK_UNICODE
[MarshalAs(UnmanagedType.LPWStr)]
public string lpVerbW;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpParametersW;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpDirectoryW;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpTitleW;
public int ptInvokeX;
public int ptInvokeY;
}
// Windows >= Vista
public static class ShellItemUtilities
{
public static readonly Guid FOLDERID_RecycleBinFolder = new Guid("b7534046-3ecb-4c18-be4e-64cd4cb7d6ac");
public static readonly Guid BHID_EnumItems = new Guid("94f60519-2850-4924-aa5a-d15e84868039");
public static readonly Guid BHID_SFUIObject = new Guid("3981e225-f559-11d3-8e3a-00c04f6837d5");
// From Windows 7
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHGetKnownFolderItem(
[In, MarshalAs(UnmanagedType.LPStruct)] Guid rfid,
uint dwFlags,
IntPtr hToken,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
out IShellItem ppv);
// For Windows Vista
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHCreateItemFromIDList(PIDLIST_ABSOLUTE pidl, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IShellItem ppv);
// For Windows Vista
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHGetKnownFolderIDList(
[In, MarshalAs(UnmanagedType.LPStruct)] Guid rfid,
uint dwFlags,
IntPtr hToken,
out PIDLIST_ABSOLUTE ppidl);
// From Windows Vista
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHGetIDListFromObject([MarshalAs(UnmanagedType.Interface)] object punk, out PIDLIST_ABSOLUTE ppidl);
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHCreateShellItemArrayFromIDLists(int cidl, [In] PIDLIST_ABSOLUTE[] rgpidl, out IShellItemArray ppsiItemArray);
public static IEnumerable<IShellItem> Enumerate(this IShellItem si)
{
object pesiTemp;
si.BindToHandler(IntPtr.Zero, BHID_EnumItems, typeof(IEnumShellItems).GUID, out pesiTemp);
var pesi = (IEnumShellItems)pesiTemp;
var items = new IShellItem[10];
while (true)
{
int fetched;
pesi.Next(1, items, out fetched);
if (fetched == 0)
{
break;
}
yield return items[0];
}
}
}
public static class ContextMenuUtilities
{
[DllImport("User32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr CreateMenu();
[DllImport("User32.dll", ExactSpelling = true, SetLastError = true)]
public static extern bool DestroyMenu(IntPtr hMenu);
[DllImport("User32.dll", ExactSpelling = true, SetLastError = true)]
public static extern int GetMenuItemCount(IntPtr hMenu);
[DllImport("User32.dll", ExactSpelling = true, SetLastError = true)]
public static extern uint GetMenuItemID(IntPtr hMenu, int nPos);
[DllImport("User32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetMenuStringW", ExactSpelling = true, SetLastError = true)]
public static extern int GetMenuString(IntPtr hMenu, uint uIDItem, [Out] StringBuilder lpString, int nMaxCount, uint uFlag);
public static string[] GetVerbs(IContextMenu cm, bool ansi = true)
{
IntPtr menu = IntPtr.Zero;
try
{
menu = CreateMenu();
// It isn't clear why short.MaxValue, but 0x7FFF is very used around the .NET!
int res = cm.QueryContextMenu(menu, 0, 0, short.MaxValue, 0);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
//var sb = new StringBuilder(128);
int count = GetMenuItemCount(menu);
var verbs = new List<string>(count);
var handle = default(GCHandle);
try
{
var bytes = new byte[ansi ? 128 : 256];
handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
for (int i = 0; i < count; i++)
{
uint id = GetMenuItemID(menu, i);
if (id == uint.MaxValue)
{
continue;
}
//GetMenuString(menu, (uint)i, sb, sb.Capacity, 0x00000400 /* MF_BYPOSITION */);
//string description = sb.ToString();
//sb.Clear();
res = cm.GetCommandString((UIntPtr)id, ansi ? (uint)0x00000002 /* GCS_VALIDATEA */ : 0x00000006 /* GCS_VALIDATEW */, IntPtr.Zero, ptr, bytes.Length);
if (res < 0)
{
continue;
}
if (res == 0)
{
res = cm.GetCommandString((UIntPtr)id, ansi ? (uint)0x00000000 /* GCS_VERBA */ : 0x00000004 /* GCS_VERBW */, IntPtr.Zero, ptr, bytes.Length);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
verbs.Add(ansi ? Marshal.PtrToStringAnsi(ptr) : Marshal.PtrToStringUni(ptr));
}
}
}
finally
{
if (handle.IsAllocated)
{
handle.Free();
}
}
return verbs.ToArray();
}
finally
{
if (menu != IntPtr.Zero)
{
DestroyMenu(menu);
}
}
}
}
And then finally a small example program that uses it... It is a small console method that will list all the files that are present in the recycle bin, put 10 of them in an array and delete them in a single operation (verb). The SHGetKnownFolderItem is from Windows 7 on, so I'm using SHGetKnownFolderIDList + SHCreateItemFromIDList that are from Windows Vista.
The code here enumerates the IShellItem files contained in the recycle bin, saves their PIDL_ABSOLUTE in a List<>, from that List<> creates a IShellItemArray, on that IShellItemArray binds a IContextMenu, for that IContextMenu executes the delete verb. Clearly by adding/not adding all the PIDL_ABSOLUTE to the List<> you can control which files will be deleted.
private static void TestShellItem()
{
IShellItem recyleBin;
string str;
IntPtr ptr = IntPtr.Zero;
int res;
//// From Windows 7
//res = ShellItemUtilities.SHGetKnownFolderItem(ShellItemUtilities.FOLDERID_RecycleBinFolder, 0, IntPtr.Zero, typeof(IShellItem).GUID, out recyleBin);
//if (res < 0)
//{
// Marshal.ThrowExceptionForHR(res);
//}
// Windows >= Vista equivalent
var pidl = default(PIDLIST_ABSOLUTE);
try
{
res = ShellItemUtilities.SHGetKnownFolderIDList(ShellItemUtilities.FOLDERID_RecycleBinFolder, 0, IntPtr.Zero, out pidl);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
res = ShellItemUtilities.SHCreateItemFromIDList(pidl, typeof(IShellItem).GUID, out recyleBin);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
}
finally
{
Marshal.FreeCoTaskMem(pidl.Ptr);
}
//// Example of use of GetDisplayName
//try
//{
// recyleBin.GetDisplayName(2, out ptr);
// str = Marshal.PtrToStringUni(ptr);
//}
//finally
//{
// if (ptr != IntPtr.Zero)
// {
// Marshal.FreeCoTaskMem(ptr);
// ptr = IntPtr.Zero;
// }
//}
var pids = new List<PIDLIST_ABSOLUTE>();
try
{
foreach (IShellItem si in recyleBin.Enumerate())
{
try
{
si.GetDisplayName(0, out ptr);
str = Marshal.PtrToStringUni(ptr);
// Some condition to include/exclude...
if (pids.Count < 10)
{
Console.WriteLine(str);
// Remember to free the pidl!
res = ShellItemUtilities.SHGetIDListFromObject(si, out pidl);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
pids.Add(pidl);
}
}
finally
{
if (ptr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(ptr);
ptr = IntPtr.Zero;
}
}
}
var pids2 = pids.ToArray();
IShellItemArray sia;
res = ShellItemUtilities.SHCreateShellItemArrayFromIDLists(pids2.Length, pids2, out sia);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
object cmTemp;
sia.BindToHandler(IntPtr.Zero, ShellItemUtilities.BHID_SFUIObject, typeof(IContextMenu).GUID, out cmTemp);
var cm = (IContextMenu)cmTemp;
// To see verbs
//var verbsAnsi = ContextMenuUtilities.GetVerbs(cm, true);
//var verbsUnicode = ContextMenuUtilities.GetVerbs(cm, false);
var cmd = new CMINVOKECOMMANDINFOEX
{
cbSize = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX)),
fMask = 0x00000400 /* CMIC_MASK_FLAG_NO_UI */,
lpVerb = "delete",
};
cm.InvokeCommand(ref cmd);
}
finally
{
foreach (var pid in pids)
{
Marshal.FreeCoTaskMem(pid.Ptr);
}
}
//// Verb executed one by one
//foreach (var item in recyleBin.Enumerate())
//{
// object cmTemp;
// item.BindToHandler(IntPtr.Zero, ShellItemUtilities.BHID_SFUIObject, typeof(IContextMenu).GUID, out cmTemp);
// var cm = (IContextMenu)cmTemp;
////// To see verbs
//// var verbsAnsi = ContextMenuUtilities.GetVerbs(cm, true);
//// var verbsUnicode = ContextMenuUtilities.GetVerbs(cm, false);
// var cmd = new CMINVOKECOMMANDINFOEX
// {
// cbSize = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX)),
// fMask = 0x00000400 /* CMIC_MASK_FLAG_NO_UI */,
// lpVerb = "delete",
// };
// cm.InvokeCommand(ref cmd);
//}
}
Code for Windows XP, uses some code from the other version, you have to take what is needed
It uses the IShellFolder interface. Note that the IContextMenu handling is exactly the same.
[StructLayout(LayoutKind.Sequential)]
public struct PIDLIST_RELATIVE
{
public IntPtr Ptr;
}
[StructLayout(LayoutKind.Sequential)]
public struct LPITEMIDLIST
{
public IntPtr Ptr;
}
[StructLayout(LayoutKind.Sequential)]
public struct PITEMID_CHILD
{
public IntPtr Ptr;
}
public enum STRRET_TYPE
{
WSTR = 0,
OFFSET = 0x1,
CSTR = 0x2
};
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 268)]
public sealed class STRRET : IDisposable
{
public STRRET_TYPE uType;
public IntPtr pOleStr;
[DllImport("Shlwapi.dll", ExactSpelling = true, SetLastError = false)]
private static extern int StrRetToBSTR(STRRET pstr, PITEMID_CHILD pidl, [MarshalAs(UnmanagedType.BStr)] out string pbstr);
~STRRET()
{
Dispose(false);
}
public override string ToString()
{
return ToString(default(PITEMID_CHILD));
}
public string ToString(PITEMID_CHILD pidl)
{
if (uType == STRRET_TYPE.WSTR)
{
if (pOleStr == IntPtr.Zero)
{
return null;
}
string str = Marshal.PtrToStringUni(pOleStr);
return str;
}
else
{
string str;
int res = StrRetToBSTR(this, pidl, out str);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
return str;
}
}
#region IDisposable Support
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
private void Dispose(bool disposing)
{
Marshal.FreeCoTaskMem(pOleStr);
pOleStr = IntPtr.Zero;
}
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214e6-0000-0000-c000-000000000046")]
public interface IShellFolder
{
void ParseDisplayName(
IntPtr hwnd,
IntPtr pbc,
[MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName,
out int pchEaten,
out PIDLIST_RELATIVE ppidl,
ref uint pdwAttributes);
void EnumObjects(IntPtr hwnd, uint grfFlags, out IEnumIDList ppenumIDList);
void BindToObject(
PIDLIST_RELATIVE pidl,
IntPtr pbc,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void BindToStorage(
PIDLIST_RELATIVE pidl,
IntPtr pbc,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
[PreserveSig]
int CompareIDs(IntPtr lParam, PIDLIST_RELATIVE pidl1, PIDLIST_RELATIVE pidl2);
void CreateViewObject(
IntPtr hwndOwner,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void GetAttributesOf(
int cidl,
[In, MarshalAs(UnmanagedType.LPArray)] LPITEMIDLIST[] apidl,
ref uint rgfInOut);
void GetUIObjectOf(
IntPtr hwndOwner,
int cidl,
[In, MarshalAs(UnmanagedType.LPArray)] PITEMID_CHILD[] apidl,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
IntPtr rgfReserved,
[MarshalAs(UnmanagedType.IUnknown)] out object ppv);
void GetDisplayNameOf(
PITEMID_CHILD pidl,
uint uFlags,
STRRET pName);
void SetNameOf(
IntPtr hwnd,
PITEMID_CHILD pidl,
[MarshalAs(UnmanagedType.LPWStr)] string pszName,
uint uFlags,
out PITEMID_CHILD ppidlOut);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214f2-0000-0000-c000-000000000046")]
public interface IEnumIDList
{
void Next(int celt, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] PITEMID_CHILD[] rgelt, out int pceltFetched);
void Skip(int celt);
void Reset();
void Clone(out IEnumIDList ppenum);
}
// Windows >= XP
public static class ShellFolderUtilities
{
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHGetSpecialFolderLocation(IntPtr hwnd, int csidl, out PIDLIST_ABSOLUTE ppidl);
[DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
public static extern int SHGetDesktopFolder(out IShellFolder ppshf);
public static readonly int CSIDL_DESKTOP = 0x0000;
public static readonly int CSIDL_BITBUCKET = 0x000a;
// https://blogs.msdn.microsoft.com/oldnewthing/20110830-00/?p=9773
public static void BindToCsidl(int csidl, Guid riid, out object ppv)
{
var pidl = default(PIDLIST_ABSOLUTE);
try
{
int res;
if (csidl != CSIDL_DESKTOP)
{
res = SHGetSpecialFolderLocation(IntPtr.Zero, csidl, out pidl);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
}
IShellFolder psfDesktop;
res = SHGetDesktopFolder(out psfDesktop);
if (res < 0)
{
Marshal.ThrowExceptionForHR(res);
}
if (csidl == CSIDL_DESKTOP)
{
ppv = psfDesktop;
return;
}
psfDesktop.BindToObject(new PIDLIST_RELATIVE { Ptr = pidl.Ptr }, IntPtr.Zero, riid, out ppv);
}
finally
{
Marshal.FreeCoTaskMem(pidl.Ptr);
}
}
public static IEnumerable<PITEMID_CHILD> Enumerate(this IShellFolder sf)
{
IEnumIDList ppenumIDList;
sf.EnumObjects(IntPtr.Zero, 0x00020 /* SHCONTF_FOLDERS */ | 0x00040 /* SHCONTF_NONFOLDERS */, out ppenumIDList);
if (ppenumIDList == null)
{
yield break;
}
var items = new PITEMID_CHILD[1];
while (true)
{
int fetched;
ppenumIDList.Next(items.Length, items, out fetched);
if (fetched == 0)
{
break;
}
yield return items[0];
}
}
}
And then finally a small example program that uses it...
private static void TestShellFolder()
{
object recycleBinTemp;
ShellFolderUtilities.BindToCsidl(ShellFolderUtilities.CSIDL_BITBUCKET, typeof(IShellFolder).GUID, out recycleBinTemp);
var recycleBin = (IShellFolder)recycleBinTemp;
var pids = new List<PITEMID_CHILD>();
try
{
foreach (PITEMID_CHILD pidl in recycleBin.Enumerate())
{
// Remember to free the pidl!
string str;
using (var ret = new STRRET { uType = STRRET_TYPE.CSTR })
{
recycleBin.GetDisplayNameOf(pidl, 0, ret);
str = ret.ToString(pidl);
}
// Some condition to include/exclude...
if (pids.Count < 10)
{
Console.WriteLine(str);
pids.Add(pidl);
}
else
{
Marshal.FreeCoTaskMem(pidl.Ptr);
}
}
var pids2 = pids.ToArray();
object cmTemp;
recycleBin.GetUIObjectOf(IntPtr.Zero, pids2.Length, pids2, typeof(IContextMenu).GUID, IntPtr.Zero, out cmTemp);
var cm = (IContextMenu)cmTemp;
// To see verbs
//var verbsAnsi = ContextMenuUtilities.GetVerbs(cm, true);
//var verbsUnicode = ContextMenuUtilities.GetVerbs(cm, false);
var cmd = new CMINVOKECOMMANDINFOEX
{
cbSize = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX)),
fMask = 0x00000400 /* CMIC_MASK_FLAG_NO_UI */,
lpVerb = "delete",
};
cm.InvokeCommand(ref cmd);
}
finally
{
foreach (var pid in pids)
{
Marshal.FreeCoTaskMem(pid.Ptr);
}
}
}
First of all add System.Runtime.InteropService to your project. We will use SHEmptyRecycleBin method, which accepts 3 parameters.
After that import the Shell32.dll in your class by using DllImport.
[DllImport("Shell32.dll")]
static extern int SHEmptyRecycleBin(IntPtr hwnd, string pszRootPath, RecycleFlag dwFlags);
Then define an enum for the RecycleBin flags(dwFlags). The values are in Hexadecimal.
enum RecycleFlag : int
{
SHERB_NOCONFIRMATION = 0x00000001, // No confirmation
SHERB_NOPROGRESSUI = 0x00000001, // No progress tracking window
SHERB_NOSOUND = 0x00000004 // No sound played
}
Place the following code in empty recycle bin button which calls the system method in Shell32.dll as:
SHEmptyRecycleBin(IntPtr.Zero, null, RecycleFlag.SHERB_NOSOUND | RecycleFlag.SHERB_NOCONFIRMATION);
reference: here
I'm trying to pinvoke a function that receives a couple of WCHAR <paramName>[1] parameters.
From what I've read in multiple places, in C/C++ you can't actually pass arrays to functions, instead they get converted to a pointer holding the first element of the array, which means that the array length becomes irrelevant and therefore WCHAR <paramName>[1] is the same as WCHAR* <paramName>.
So normally I'd declare this as a StringBuilder in C# and marshal it as LPWStr, but in this particular case that throws all sorts of errors.
Basically, the function above receives a GET_VIRTUAL_DISK_INFO, which in turn holds a union of some structs and other loose fields. And it is a couple of the structs in the union that receive a WCHAR <paramName>[1].
When I try to marshal it as LPWStr and declare my field as StringBuilder or string I get the error [...] contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.
I've also tried declaring it as an IntPtr and then using Marshal.PtrToStringAuto(<IntPtr>) but I get an empty string.
My C# code with all the structs, enums etc:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Permissions;
namespace ConsoleApplication1
{
class Program
{
public static Guid VirtualStorageTypeVendorMicrosoft = new Guid("EC984AEC-A0F9-47e9-901F-71415A66345B");
static void Main(string[] args)
{
var handle = new VirtualDiskSafeHandle();
var storageType = new VirtualStorageType
{
DeviceId = VirtualStorageDeviceType.Vhdx,
VendorId = VirtualStorageTypeVendorMicrosoft
};
var parameters = new OpenVirtualDiskParameters
{
Version = OpenVirtualDiskVersion.Version2
};
var result = OpenVirtualDisk(ref storageType, "D:\\Test2.vhdx", VirtualDiskAccessMask.None, OpenVirtualDiskFlag.None,
ref parameters, ref handle);
if (result != 0)
{
throw new Win32Exception((int) result);
}
var info = new GetVirtualDiskInfo {Version = GetVirtualDiskInfoVersion.PhysicalDisk};
var infoSize = (uint) Marshal.SizeOf(info);
uint sizeUsed = 0;
result = GetVirtualDiskInformation(handle, ref infoSize, ref info, ref sizeUsed);
if (result != 0)
{
throw new Win32Exception((int) result);
}
Console.WriteLine($"LogicalSizeSector = {info.Union.PhysicalDisk.LogicalSectorSize}");
Console.WriteLine($"PhysicalSizeSector = {info.Union.PhysicalDisk.PhysicalSectorSize}");
Console.ReadLine();
}
[DllImport("virtdisk.dll", CharSet = CharSet.Unicode)]
public static extern uint OpenVirtualDisk(
[In] ref VirtualStorageType virtualStorageType,
[In] string path,
[In] VirtualDiskAccessMask virtualDiskAccessMask,
[In] OpenVirtualDiskFlag flags,
[In] ref OpenVirtualDiskParameters parameters,
[In, Out] ref VirtualDiskSafeHandle handle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(
[In] IntPtr hObject);
[DllImport("virtdisk.dll", CharSet = CharSet.Unicode)]
public static extern uint GetVirtualDiskInformation(
[In] VirtualDiskSafeHandle virtualDiskHandle,
[In, Out] ref uint virtualDiskInfoSize,
[In, Out] ref GetVirtualDiskInfo virtualDiskInfo,
[In, Out] ref uint sizeUsed);
[SecurityPermission(SecurityAction.Demand)]
public class VirtualDiskSafeHandle : SafeHandle
{
public VirtualDiskSafeHandle() : base(IntPtr.Zero, true) { }
public override bool IsInvalid => IsClosed || (handle == IntPtr.Zero);
public bool IsOpen => !IsInvalid;
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
public override string ToString()
{
return handle.ToString();
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct OpenVirtualDiskParameters
{
public OpenVirtualDiskVersion Version; //OPEN_VIRTUAL_DISK_VERSION
public OpenVirtualDiskParametersUnion Union;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct OpenVirtualDiskParametersUnion
{
[FieldOffset(0)]
public OpenVirtualDiskParametersVersion1 Version1;
[FieldOffset(0)]
public OpenVirtualDiskParametersVersion2 Version2;
[FieldOffset(0)]
public OpenVirtualDiskParametersVersion3 Version3;
}
/// <summary>
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct OpenVirtualDiskParametersVersion1
{
public uint RWDepth; //ULONG
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct OpenVirtualDiskParametersVersion2
{
public bool GetInfoOnly; //BOOL
public bool ReadOnly; //BOOL
public Guid ResiliencyGuid; //GUID
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct OpenVirtualDiskParametersVersion3
{
public bool GetInfoOnly; //BOOL
public bool ReadOnly; //BOOL
public Guid ResiliencyGuid; //GUID
public Guid SnapshotId; //GUID
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfo
{
public GetVirtualDiskInfoVersion Version; //GET_VIRTUAL_DISK_INFO_VERSION
public GetVirtualDiskInfoUnion Union;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoUnion
{
[FieldOffset(0)] public GetVirtualDiskInfoSize Size;
[FieldOffset(0)] public Guid Identifier; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoParentLocation ParentLocation;
[FieldOffset(0)] public Guid ParentIdentifier; //GUID
[FieldOffset(0)] public uint ParentTimestamp; //ULONG
[FieldOffset(0)] public VirtualStorageType VirtualStorageType; //VIRTUAL_STORAGE_TYPE
[FieldOffset(0)] public uint ProviderSubtype; //ULONG
[FieldOffset(0)] public bool Is4kAligned; //BOOL
[FieldOffset(0)] public bool IsLoaded; //BOOL
[FieldOffset(0)] public GetVirtualDiskInfoPhysicalDisk PhysicalDisk;
[FieldOffset(0)] public uint VhdPhysicalSectorSize; //ULONG
[FieldOffset(0)] public ulong SmallestSafeVirtualSize; //ULONGLONG
[FieldOffset(0)] public uint FragmentationPercentage; //ULONG
[FieldOffset(0)] public Guid VirtualDiskId; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoChangeTrackingState ChangeTrackingState;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoSize
{
public ulong VirtualSize; //ULONGLONG
public ulong PhysicalSize; //ULONGLONG
public uint BlockSize; //ULONG
public uint SectorSize; //ULONG
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoParentLocation
{
public bool ParentResolved; //BOOL
public IntPtr ParentLocationBuffer; //WCHAR[1]
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoPhysicalDisk
{
public uint LogicalSectorSize; //ULONG
public uint PhysicalSectorSize; //ULONG
public bool IsRemote; //BOOL
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoChangeTrackingState
{
public bool Enabled; //BOOL
public bool NewerChanges; //BOOL
public IntPtr MostRecentId; //WCHAR[1]
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct VirtualStorageType
{
public VirtualStorageDeviceType DeviceId; //ULONG
public Guid VendorId; //GUID
}
public enum GetVirtualDiskInfoVersion
{
Unspecified = 0,
Size = 1,
Identifier = 2,
ParentLocation = 3,
ParentIdentifier = 4,
ParentTimestamp = 5,
VirtualStorageType = 6,
ProviderSubtype = 7,
Is4KAligned = 8,
PhysicalDisk = 9,
VhdPhysicalSectorSize = 10,
SmallestSafeVirtualSize = 11,
Fragmentation = 12,
IsLoaded = 13,
VirtualDiskId = 14,
ChangeTrackingState = 15
}
public enum VirtualStorageDeviceType
{
Unknown = 0,
Iso = 1,
Vhd = 2,
Vhdx = 3,
Vhdset = 4
}
public enum VirtualDiskAccessMask
{
None = 0x00000000,
AttachRo = 0x00010000,
AttachRw = 0x00020000,
Detach = 0x00040000,
GetInfo = 0x00080000,
Create = 0x00100000,
Metaops = 0x00200000,
Read = 0x000d0000,
All = 0x003f0000,
Writable = 0x00320000
}
[Flags]
public enum OpenVirtualDiskFlag
{
None = 0x00000000,
NoParents = 0x00000001,
BlankFile = 0x00000002,
BootDrive = 0x00000004,
CachedIo = 0x00000008,
CustomDiffChain = 0x00000010,
ParentCachedIo = 0x00000020,
VhdsetFileOnly = 0x00000040
}
public enum OpenVirtualDiskVersion
{
Unspecified = 0,
Version1 = 1,
Version2 = 2,
Version3 = 3,
}
}
}
Please comment out the line:
[FieldOffset(0)] public GetVirtualDiskInfoChangeTrackingState ChangeTrackingState;
Without that the struct results will be completely screwed up, still trying to figure out why.
Marshal the struct from the function call as an IntPtr. You will need to use Marshal.AllocHGlobal or another similar technique to get a block of unmanaged memory, since the Marshal isn't going to do it for you. You can then load the size member manually, or using Marshal.StructureToPtr.
From there use Marshal.OffsetOf to get the offset to the Union member. Once that's done use Marshal.Read and Marshal.PtrToStringUni to get at the data. For example with the Parent Location information:
IntPtr raw = Marshal.AllocHGlobal(1024);
// This is the GetVirtualDiskInfo from your provided code.
GetVirtualDiskInfo info = new GetVirtualDiskInfo();
info.Version = GetVirtualDiskInfoVersion.ParentLocation;
Marshal.StructureToPtr(info, raw, true);
Class1.Test(raw); // Replace this with your call to the function,
// This is a call to a C++/CLI method I wrote to stuff data
// into the structure.
IntPtr offsetToUnion = Marshal.OffsetOf(typeof(GetVirtualDiskInfo), "Union");
IntPtr data = raw + offsetToUnion.ToInt32();
bool parentResolved = Marshal.ReadInt32(data) != 0;
string parentLocationBuffer = Marshal.PtrToStringUni(data + 4);
Marshal.FreeHGlobal(raw); // Don't forget this!
Here's the method in the C++/CLI that loads the data for testing:
static void Test(IntPtr ptr)
{
GET_VIRTUAL_DISK_INFO* info = (GET_VIRTUAL_DISK_INFO*)ptr.ToPointer();
info->ParentLocation.ParentResolved = TRUE;
memcpy(info->ParentLocation.ParentLocationBuffer, L"123456789", 20);
}
I am trying to get raw input data from a Surface touch screen, using RAWINPUT API.
Getting the devices list works good. But when I want to register the touch screen device with RegisterRawInputDevices, I have issues : the function returns false with 87 (ERROR_INVALID_PARAMETER) in GetLastError... I have tried different things that I read online but none work.
I am using Interop on C# for very simple desktop console application, developping with Visual Studio 2013 on Windows 8.1. The target is a Surface Pro 3.
The code doing the work with the API is hereunder... (sorry for the ugly editing).
I will be so thanksful for your help :)
Pierre
CLASS for the API wrapping in C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace ConsoleApplication3
{
class APIWrapper
{
#region Properties
private static int _vendorID=-1;
public static int VendorID
{
get { return _vendorID; }
}
private static int _productID=-1;
public static int ProductID
{
get { return _productID; }
}
#endregion
#region API structs
internal struct RAWINPUTDEVICELIST_ELMT
{
public IntPtr hDevice;
public uint dwType;
}
public enum RawInputDeviceType : uint
{
RIM_TYPEMOUSE = 0,
RIM_TYPEKEYBOARD = 1,
RIM_TYPEHID = 2,
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWINPUTDEVICE
{
public ushort usUsagePage;
public ushort usUsage;
public int dwFlags;
public IntPtr hwndTarget;
}
public enum RawInputDeviceInfoType : uint
{
RIDI_DEVICENAME = 0x20000007,
RIDI_DEVICEINFO = 0x2000000b,
RIDI_PREPARSEDDATA = 0x20000005,
}
public const ushort RIDEV_INPUTSINK =0x00000100;
public const ushort RIDEV_PAGEONLY = 0x00000020;
[StructLayout(LayoutKind.Sequential)]
internal struct RID_DEVICE_INFO_HID
{
public int dwVendorId;
public int dwProductId;
public int dwVersionNumber;
public ushort usUsagePage;
public ushort usUsage;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RID_DEVICE_INFO_MOUSE
{
public int dwId;
public int dwNumberOfButtons;
public int dwSampleRate;
public bool fHasHorizontalWheel;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RID_DEVICE_INFO_KEYBOARD
{
public int dwType;
public int dwSubType;
public int dwKeyboardMode;
public int dwNumberOfFunctionKeys;
public int dwNumberOfIndicators;
public int dwNumberOfKeysTotal;
}
[StructLayout(LayoutKind.Explicit)]
internal struct RID_DEVICE_INFO
{
[FieldOffset(0)]
public uint cbSize;
[FieldOffset(4)]
public RawInputDeviceType dwType;
[FieldOffset(8)]
public RID_DEVICE_INFO_MOUSE mouse;
[FieldOffset(8)]
public RID_DEVICE_INFO_KEYBOARD keyboard;
[FieldOffset(8)]
public RID_DEVICE_INFO_HID hid;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWINPUTHEADER
{
public RawInputDeviceType dwType;
public int dwSize;
public IntPtr hDevice;
public uint wParam;
}
[StructLayout(LayoutKind.Explicit)]
internal struct RAWMOUSE
{
[FieldOffset(0)]
public ushort usFlags;
[FieldOffset(2)]
public uint ulButtons;
[FieldOffset(4)]
public ushort usButtonFlags;
[FieldOffset(2)]
public ushort usButtonData;
[FieldOffset(6)]
public uint ulRawButtons;
[FieldOffset(10)]
public int lLastX;
[FieldOffset(14)]
public int lLastY;
[FieldOffset(18)]
public uint ulExtraInformation;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWKEYBOARD
{
public ushort MakeCode;
public ushort Flags;
public ushort Reserved;
public ushort VKey;
public uint Message;
public uint ExtraInformation;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWHID
{
public int dwSizeHid;
public int dwCount;
//use of a pointer here for struct reason
public IntPtr pbRawData;
}
[StructLayout(LayoutKind.Explicit)]
internal struct RAWINPUT
{
[FieldOffset(0)]
public RAWINPUTHEADER header;
[FieldOffset(16 + 8)]
public RAWMOUSE mouse;
[FieldOffset(16 + 8)]
public RAWKEYBOARD keyboard;
[FieldOffset(16 + 8)]
public RAWHID hid;
}
#endregion
#region API functions
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetRawInputDeviceList([In, Out] RAWINPUTDEVICELIST_ELMT[] InputdeviceList, [In, Out] ref uint puiNumDevices, [In] uint cbSize);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetRawInputDeviceInfo([In] IntPtr hDevice, [In] RawInputDeviceInfoType uiCommand, [In, Out] IntPtr pData, [In, Out] ref uint pcbSize);
[DllImport("user32.dll", SetLastError = true)]
static extern bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevices, uint uiNumDevices, uint cbSize);
[DllImport("user32.dll")]
static extern uint GetRegisteredRawInputDevices([In, Out] RAWINPUTDEVICE[] InputdeviceList, [In, Out] ref uint puiNumDevices, [In] uint cbSize);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetRawInputBuffer([In,Out] RAWINPUT[] pData, [In, Out] ref uint pcbSize, [In] uint cbSizeHeader);
#endregion
#region Methods for devices
public static bool GetRawInputDeviceList()
{
bool res = false;
RAWINPUTDEVICELIST_ELMT[] pRawInputDeviceList = null;
uint puiNumDevices = 0;
uint returnCode = GetRawInputDeviceList(pRawInputDeviceList, ref puiNumDevices, (uint)Marshal.SizeOf(new RAWINPUTDEVICELIST_ELMT()));
res = (0xFFFFFFFF != returnCode);
if (res)
{
//alloc array
pRawInputDeviceList = new RAWINPUTDEVICELIST_ELMT[puiNumDevices];
//get devices
returnCode = GetRawInputDeviceList(pRawInputDeviceList, ref puiNumDevices, (uint)Marshal.SizeOf((typeof(RAWINPUTDEVICELIST_ELMT))));
res = (0xFFFFFFFF != returnCode);
if (res)
{
//look for the touchscreen.
bool foundTouchScreen = false;
foreach (RAWINPUTDEVICELIST_ELMT rawInputDevice in pRawInputDeviceList)
{
//IntPtr pData = IntPtr.Zero;
//uint strsize = 0;
//IntPtr deviceHandle = rawInputDevice.hDevice;
//returnCode = GetRawInputDeviceInfo(deviceHandle, RawInputDeviceInfoType.RIDI_DEVICENAME, pData, ref strsize);
//pData = Marshal.AllocHGlobal((int)strsize);
//returnCode = GetRawInputDeviceInfo(deviceHandle, RawInputDeviceInfoType.RIDI_DEVICENAME, pData, ref strsize);
////Console.WriteLine("Result = " + returnCode + " ErrorCode = " + Marshal.GetLastWin32Error());
//string name = Marshal.PtrToStringAnsi(pData);
//Console.WriteLine("Name = " + name);
uint structsize = (uint)Marshal.SizeOf(typeof(RID_DEVICE_INFO));
RID_DEVICE_INFO di = new RID_DEVICE_INFO();
di.cbSize = structsize;
IntPtr pData = Marshal.AllocHGlobal((int)structsize);
returnCode = GetRawInputDeviceInfo(rawInputDevice.hDevice, RawInputDeviceInfoType.RIDI_DEVICEINFO, pData, ref structsize);
if (0xFFFFFFF != returnCode && 0 != returnCode)
{
di = (RID_DEVICE_INFO)Marshal.PtrToStructure(pData, typeof(RID_DEVICE_INFO));
//Console.WriteLine("di.dwType = " + Enum.GetName(typeof(RawInputDeviceType), di.dwType));
switch (di.dwType)
{
case RawInputDeviceType.RIM_TYPEHID:
/* Console.WriteLine("di.hid.dwVendorId = " + di.hid.dwVendorId);
Console.WriteLine("di.hid.dwProductId = " + di.hid.dwProductId);
Console.WriteLine("di.hid.dwVersionNumber = " + di.hid.dwVersionNumber);
Console.WriteLine("di.hid.usUsagePage = " + di.hid.usUsagePage);
Console.WriteLine("di.hid.usUsage = " + di.hid.usUsage);*/
if (0x0D == di.hid.usUsagePage && 0x04 == di.hid.usUsage)
{
_vendorID = di.hid.dwVendorId;
_productID = di.hid.dwProductId;
foundTouchScreen = true;
}
break;
case RawInputDeviceType.RIM_TYPEKEYBOARD:
case RawInputDeviceType.RIM_TYPEMOUSE:
default:
break;
}
if (foundTouchScreen)
{
RAWINPUTDEVICE[] rawInputDevicesToMonitor = new RAWINPUTDEVICE[1];
RAWINPUTDEVICE device = new RAWINPUTDEVICE();
device.dwFlags =RIDEV_INPUTSINK;
device.hwndTarget = Process.GetCurrentProcess().MainWindowHandle;
device.usUsage = di.hid.usUsage;
device.usUsagePage = di.hid.usUsagePage;
rawInputDevicesToMonitor[0] = device;
if (!RegisterRawInputDevices(rawInputDevicesToMonitor, (uint)1, (uint)Marshal.SizeOf(new RAWINPUTDEVICE())))
{
Console.WriteLine("Registration of device --> NOK (error: " + Marshal.GetLastWin32Error()+")");
RAWINPUTDEVICE [] pRegisteredRawInputDeviceList = null;
uint puiNumRegDevices = 0;
returnCode = GetRegisteredRawInputDevices(pRegisteredRawInputDeviceList, ref puiNumRegDevices, (uint)Marshal.SizeOf(new RAWINPUTDEVICE()));
res = (0xFFFFFFFF != returnCode);
if (res)
{
//alloc array
pRegisteredRawInputDeviceList = new RAWINPUTDEVICE[puiNumRegDevices];
//get devices
returnCode = GetRegisteredRawInputDevices(pRegisteredRawInputDeviceList, ref puiNumRegDevices, (uint)Marshal.SizeOf((typeof(RAWINPUTDEVICE))));
Console.WriteLine("Registered devices nb : " + returnCode);
}
}
break;
}
}
}
}
/*Need to set RIDEV_INPUTSINK in the dwFlags member and set hwndTarget to be able to make use of this function otherwise
* GetRawInputBuffer will ALWAYS say 0 when you try to find out what size you need the buffer to be.
*/
}
/*else
int error = Marshal.GetLastWin32Error();*/
return res;
}
#endregion
}
}
MAIN CLASS:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
//look for the touch screen device
if (!APIWrapper.GetRawInputDeviceList())
{
Console.WriteLine("Error occured when getting RawInputDevice List.");
}
if (APIWrapper.VendorID == -1 && APIWrapper.ProductID == -1)
{
Console.WriteLine("No Touchscreen device has been found.");
}
else
{
Console.WriteLine("Touchscreen found : VendorID=" + APIWrapper.VendorID + " ProductID=" + APIWrapper.ProductID);
//wait for rawinputdata
Console.WriteLine("Waiting data loop : started.");
uint dataNb = APIWrapper.ReadHIDData();
switch (dataNb)
{
case 0xFFFFFFFF:
Console.WriteLine("An error occured.");
break;
case 0:
Console.WriteLine("No data received.");
break;
default:
Console.WriteLine(dataNb + " rawInputData received");
break;
}
Console.WriteLine("Waiting data loop : ended.");
}
Console.ReadLine();
}
}
}
Try to use mfakane/rawinput-sharp: C# wrapper library for Raw Input
I'm trying to use CryptoAPI from C# code to add SHA256 timestamps to signed assemblies. Here is the code I'm using:
Signer.TimestampSignedAssembly("MyAssembly.exe", "http://tsa.starfieldtech.com");
Signer class:
public static class Signer
{
[StructLayoutAttribute(LayoutKind.Sequential)]
struct SIGNER_SUBJECT_INFO
{
public uint cbSize;
public IntPtr pdwIndex;
public uint dwSubjectChoice;
public SubjectChoiceUnion Union1;
[StructLayoutAttribute(LayoutKind.Explicit)]
internal struct SubjectChoiceUnion
{
[FieldOffsetAttribute(0)]
public IntPtr pSignerFileInfo;
[FieldOffsetAttribute(0)]
public IntPtr pSignerBlobInfo;
}
}
[StructLayoutAttribute(LayoutKind.Sequential)]
struct SIGNER_FILE_INFO
{
public uint cbSize;
public IntPtr pwszFileName;
public IntPtr hFile;
}
[DllImport("Mssign32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int SignerTimeStampEx2(
uint dwFlags, // DWORD
IntPtr pSubjectInfo, // SIGNER_SUBJECT_INFO
string pwszHttpTimeStamp, // LPCWSTR
uint dwAlgId, // ALG_ID
IntPtr psRequest, // PCRYPT_ATTRIBUTES
IntPtr pSipData, // LPVOID
out IntPtr ppSignerContext // SIGNER_CONTEXT
);
public static void TimestampSignedAssembly(string appPath, string tsaServer)
{
if (tsaServer == null) throw new ArgumentNullException("tsaServer");
var pSubjectInfo = IntPtr.Zero;
try
{
pSubjectInfo = CreateSignerSubjectInfo(appPath);
TimestampSignedAssembly(pSubjectInfo, tsaServer);
}
finally
{
if (pSubjectInfo != IntPtr.Zero)
{
Marshal.DestroyStructure(pSubjectInfo, typeof(SIGNER_SUBJECT_INFO));
}
}
}
private static IntPtr CreateSignerSubjectInfo(string pathToAssembly)
{
var info = new SIGNER_SUBJECT_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_SUBJECT_INFO)),
pdwIndex = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint)))
};
var index = 0;
Marshal.StructureToPtr(index, info.pdwIndex, false);
info.dwSubjectChoice = 0x1; //SIGNER_SUBJECT_FILE
var assemblyFilePtr = Marshal.StringToHGlobalUni(pathToAssembly);
var fileInfo = new SIGNER_FILE_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_FILE_INFO)),
pwszFileName = assemblyFilePtr,
hFile = IntPtr.Zero
};
info.Union1 = new SIGNER_SUBJECT_INFO.SubjectChoiceUnion
{
pSignerFileInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIGNER_FILE_INFO)))
};
Marshal.StructureToPtr(fileInfo, info.Union1.pSignerFileInfo, false);
IntPtr pSubjectInfo = Marshal.AllocHGlobal(Marshal.SizeOf(info));
Marshal.StructureToPtr(info, pSubjectInfo, false);
return pSubjectInfo;
}
/*
Here CryptoAPI function SignerTimeStampEx2 called.
*/
private static void TimestampSignedAssembly(IntPtr pSubjectInfo, string tsaServer)
{
IntPtr context;
var hResult = SignerTimeStampEx2(
0x1, // I have not found anywhere what value should have this parameter!
pSubjectInfo,
tsaServer,
0x0000800c, // 256 bit SHA hashing algorithm. This value taken form here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
IntPtr.Zero,
IntPtr.Zero,
out context
);
if (hResult != 0)
{
throw new Exception(string.Format("Error occured when adding timestamp - Error code: 0x{0:X}", hResult));
}
}
}
Despite the fact that I pass to SignerTimeStampEx2 function an argument (dwAlgId), indicating that it is necessary to add SHA256 timestamp (0x0000800c), SHA1 timestamp is always generated.
Has anyone encountered with this problem? What I'm doing wrong? What values should I set for dwFlags and dwAlgId parameters?
Thanks in advance!
dwFlags needs to be SIGNER_TIMESTAMP_RFC3161 (2). The reason you get an access violation is that SignerTimeStampEx2() is documented incorrectly. It expects the algorithm as a PCSTR rather than a DWORD. If you pass 0x800C it'll try to dereference that as a pointer, leading to the AV. So replace ALG_ID dwAlgId in the function declaration with PCSTR pszTimeStampAlgorithmOid. Pass szOID_NIST_sha256 to it, which should be defined as "2.16.840.1.101.3.4.2.1".
SignerTimeStampEx3() is also incorrectly incorrectly documented. pszTimeStampAlgorithmOid should be declared as PCSTR rather than as PCWSTR.
In my experience, code signing and time stamping are more reliable if you specify both the file name and an open Win32 file handle in the SIGNER_FILE_INFO structure.
Whether you will actually get an SHA-256 time stamp also depends on the time stamping service you're using. http://tsa.starfieldtech.com, http://timestamp.globalsign.com/ and http://timestamp.comodoca.com/rfc3161 issue SHA-256 timestamps. Other services may issue SHA-1 time stamps even when requesting an SHA-256 time stamp.
I got it working finally. Here is the complete code of the Timestamper class:
public static class Timestamper
{
[StructLayout(LayoutKind.Sequential)]
struct SIGNER_SUBJECT_INFO
{
public uint cbSize;
public IntPtr pdwIndex;
public uint dwSubjectChoice;
public SubjectChoiceUnion Union1;
[StructLayoutAttribute(LayoutKind.Explicit)]
internal struct SubjectChoiceUnion
{
[FieldOffsetAttribute(0)]
public IntPtr pSignerFileInfo;
[FieldOffsetAttribute(0)]
public IntPtr pSignerBlobInfo;
}
}
[StructLayoutAttribute(LayoutKind.Sequential)]
struct SIGNER_FILE_INFO
{
public uint cbSize;
public IntPtr pwszFileName;
public IntPtr hFile;
}
[DllImport("Mssign32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int SignerTimeStampEx2(
uint dwFlags, // DWORD
IntPtr pSubjectInfo, // SIGNER_SUBJECT_INFO
string pwszHttpTimeStamp, // LPCWSTR
IntPtr pszTimeStampAlgorithmOid, // PCSTR
IntPtr psRequest, // PCRYPT_ATTRIBUTES
IntPtr pSipData, // LPVOID
out IntPtr ppSignerContext // SIGNER_CONTEXT
);
public static void TimestampSignedAssembly(string appPath, string tsaServer)
{
if (tsaServer == null) throw new ArgumentNullException("tsaServer");
IntPtr pSubjectInfo = IntPtr.Zero;
try
{
pSubjectInfo = CreateSignerSubjectInfo(appPath);
TimestampSignedAssembly(pSubjectInfo, tsaServer);
}
finally
{
if (pSubjectInfo != IntPtr.Zero)
{
Marshal.DestroyStructure(pSubjectInfo, typeof(SIGNER_SUBJECT_INFO));
}
}
}
private static IntPtr CreateSignerSubjectInfo(string pathToAssembly)
{
SIGNER_SUBJECT_INFO info = new SIGNER_SUBJECT_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_SUBJECT_INFO)),
pdwIndex = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint)))
};
int index = 0;
Marshal.StructureToPtr(index, info.pdwIndex, false);
info.dwSubjectChoice = 0x1; //SIGNER_SUBJECT_FILE
IntPtr assemblyFilePtr = Marshal.StringToHGlobalUni(pathToAssembly);
SIGNER_FILE_INFO fileInfo = new SIGNER_FILE_INFO
{
cbSize = (uint)Marshal.SizeOf(typeof(SIGNER_FILE_INFO)),
pwszFileName = assemblyFilePtr,
hFile = IntPtr.Zero
};
info.Union1 = new SIGNER_SUBJECT_INFO.SubjectChoiceUnion
{
pSignerFileInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIGNER_FILE_INFO)))
};
Marshal.StructureToPtr(fileInfo, info.Union1.pSignerFileInfo, false);
IntPtr pSubjectInfo = Marshal.AllocHGlobal(Marshal.SizeOf(info));
Marshal.StructureToPtr(info, pSubjectInfo, false);
return pSubjectInfo;
}
/*
Here CryptoAPI function SignerTimeStampEx2 called.
*/
private static void TimestampSignedAssembly(IntPtr pSubjectInfo, string tsaServer)
{
IntPtr context;
int hResult = SignerTimeStampEx2(
0x2, // SIGNER_TIMESTAMP_RFC3161
pSubjectInfo,
tsaServer,
Marshal.StringToHGlobalAnsi("2.16.840.1.101.3.4.2.1"), // szOID_NIST_sha256 constant, SHA256 hashing algorithm.
IntPtr.Zero,
IntPtr.Zero,
out context
);
if (hResult != 0)
{
throw new Exception(string.Format("Error occured when adding timestamp - Error code: 0x{0:X}", hResult));
}
}
}
Usage example:
Timestamper.TimestampSignedAssembly("Assembly.exe", "http://timestamp.comodoca.com/?td=sha256");