I've been using Mapi32 from a Winforms app to send bring up a new mail message with attachments for a while now, and it's worked really well. (Yes, I'm aware that calling into MAPI32 from C# is not supported.)
Within the last few days, it has stopped working when Outlook is running. However, if Outlook is not running, it will work as expected. This happens in both Vista and XP.
Have any SOer's had this problem? How did you resolve it?
Here's the code I've been using:
public class EmailController
{
[DllImport("MAPI32.DLL", CharSet = CharSet.Ansi)]
public static extern int MAPISendMail(IntPtr lhSession, IntPtr ulUIParam,
MapiMessage lpMessage, int flFlags, int ulReserved);
public const int MAPI_LOGON_UI = 0x00000001;
private const int MAPI_DIALOG = 0x00000008;
public static int SendMail(string strAttachmentFileName, string strSubject,string to)
{
IntPtr session = new IntPtr(0);
IntPtr winhandle = new IntPtr(0);
MapiMessage msg = new MapiMessage();
msg.subject = strSubject;
int sizeofMapiDesc = Marshal.SizeOf(typeof(MapiFileDesc));
IntPtr pMapiDesc = Marshal.AllocHGlobal(sizeofMapiDesc);
MapiFileDesc fileDesc = new MapiFileDesc();
fileDesc.position = -1;
int ptr = (int)pMapiDesc;
string path = strAttachmentFileName;
fileDesc.name = Path.GetFileName(path);
fileDesc.path = path;
Marshal.StructureToPtr(fileDesc, (IntPtr)ptr, false);
msg.files = pMapiDesc;
msg.fileCount = 1;
List<MapiRecipDesc> recipsList = new List<MapiRecipDesc>();
MapiRecipDesc recipient = new MapiRecipDesc();
recipient.recipClass = 1;
recipient.name = to;
recipsList.Add(recipient);
int size = Marshal.SizeOf(typeof(MapiRecipDesc));
IntPtr intPtr = Marshal.AllocHGlobal(recipsList.Count * size);
int recipPtr = (int)intPtr;
foreach (MapiRecipDesc mapiDesc in recipsList)
{
Marshal.StructureToPtr(mapiDesc, (IntPtr)recipPtr, false);
recipPtr += size;
}
msg.recips = intPtr;
msg.recipCount = 1;
int result = MAPISendMail(session, winhandle, msg, MAPI_LOGON_UI | MAPI_DIALOG, 0);
return result;
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiMessage
{
public int reserved;
public string subject;
public string noteText;
public string messageType;
public string dateReceived;
public string conversationID;
public int flags;
public IntPtr originator;
public int recipCount;
public IntPtr recips;
public int fileCount;
public IntPtr files;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiFileDesc
{
public int reserved;
public int flags;
public int position;
public string path;
public string name;
public IntPtr type;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiRecipDesc
{
public int reserved;
public int recipClass;
public string name;
public string address;
public int eIDSize;
public IntPtr entryID;
}
If you're application is running with Elevated Privileges (i.e. as Administrator) and Outlook isn't, the send will fail. You will have to tell the user to close all running instances of Outlook and try again.
int result = MAPISendMail(session, winhandle, msg, MAPI_LOGON_UI | MAPI_DIALOG, 0);
return result;
What does it return?
Well, other than the obvious "use supported C# mail-sending methods" comment, I wonder if something happened to the mapi32.dll file - have you tried a "Detect and Repair" in Outlook?
I also read here (http://office.microsoft.com/en-us/outlook/HP011164781033.aspx) that there are some steps you can do (the post and the comments) to get Outlook to repair or replace the dll.
Related
How would you write this type of struct in c#?
struct _JOBOBJECT_BASIC_PROCESS_ID_LIST {
DWORD NumberOfAssignedProcesses;
DWORD NumberOfProcessIdsInList;
ULONG_PTR ProcessIdList[1];
}
sins there is no set size for the ProcessIdList array, what do you do? Do you just write it like this:
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
int NumberOfAssignedProcesses;
int NumberOfProcessIdsInList;
IntPtr ProcessIdList; //Must point to a allocated array, thanks jdweng for letting me know.
}
or do you just assign a size which is big enough, e.g.:
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
int NumberOfAssignedProcesses;
int NumberOfProcessIdsInList;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_PATH)]
UIntPtr[] ProcessIdList; //Works just fine, but is limited to the SizeConst.
}
This sort of structure is usually declared (there are others like this one in WLan APIs for example) :
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
public int NumberOfAssignedProcesses;
public int NumberOfProcessIdsInList;
public IntPtr[] ProcessIdList;
public JOBOBJECT_BASIC_PROCESS_ID_LIST(IntPtr pList)
{
int nIntSize = Marshal.SizeOf<int>(); // 4
NumberOfAssignedProcesses = Marshal.ReadInt32(pList, 0);
NumberOfProcessIdsInList = Marshal.ReadInt32(pList, nIntSize);
ProcessIdList = new IntPtr[NumberOfProcessIdsInList];
for (int i = 0; i < NumberOfProcessIdsInList; i++)
{
IntPtr pItemList = IntPtr.Zero;
if (Marshal.SizeOf<IntPtr>() == 4)
pItemList = new IntPtr(pList.ToInt32() + (i * Marshal.SizeOf<IntPtr>()) + (nIntSize * 2));
else
pItemList = new IntPtr(pList.ToInt64() + (i * Marshal.SizeOf<IntPtr>()) + (nIntSize * 2));
IntPtr nPID = new IntPtr();
nPID = Marshal.ReadIntPtr(pItemList, 0);
ProcessIdList[i] = nPID;
}
}
}
A test with 5 Notepad launched and assigned to a job with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
then QueryInformationJobObject to enumerate the PIDs by using this structure =>
private IntPtr hJob = IntPtr.Zero;
bool bRet = false;
hJob = CreateJobObject(IntPtr.Zero, "Test Job Object");
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jbeli = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
jbeli.BasicLimitInformation.LimitFlags |= (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_BREAKAWAY_OK);
int nLength = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
IntPtr pJobInfo = Marshal.AllocHGlobal(nLength);
Marshal.StructureToPtr(jbeli, pJobInfo, false);
SetInformationJobObject(hJob, JOBOBJECTINFOCLASS.JobObjectExtendedLimitInformation, pJobInfo, (uint)nLength);
Marshal.FreeHGlobal(pJobInfo);
int nNbProcesses = 5;
for (int i = 0; i < nNbProcesses; i++)
{
using (Process exeProcess = new Process())
{
exeProcess.StartInfo.FileName = "notepad";
exeProcess.Start();
exeProcess.WaitForInputIdle();
IntPtr hProcess = exeProcess.Handle;
bRet = AssignProcessToJobObject(hJob, hProcess);
}
}
JOBOBJECT_BASIC_PROCESS_ID_LIST jobpil = new JOBOBJECT_BASIC_PROCESS_ID_LIST();
jobpil.NumberOfAssignedProcesses = nNbProcesses;
int nSize = Marshal.SizeOf<JOBOBJECT_BASIC_PROCESS_ID_LIST>() + (nNbProcesses - 1) * Marshal.SizeOf<IntPtr>();
IntPtr pJobpil = Marshal.AllocHGlobal(nSize);
Marshal.StructureToPtr(jobpil, pJobpil, false);
int nReturnLength = 0;
bRet = QueryInformationJobObject(hJob, JOBOBJECTINFOCLASS.JobObjectBasicProcessIdList, pJobpil, nSize, out nReturnLength);
if (bRet)
{
var processidlist = new JOBOBJECT_BASIC_PROCESS_ID_LIST(pJobpil);
foreach (var pid in processidlist.ProcessIdList)
{
Console.WriteLine("PID: {0}", pid.ToString());
}
}
else
{
int nErr = Marshal.GetLastWin32Error();
Win32Exception win32Exception = new Win32Exception(nErr);
this.Activate();
MessageBox.Show("Error: " + win32Exception.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Marshal.FreeHGlobal(pJobpil);
// CloseHandle can be added in Form1_FormClosed :
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
CloseHandle(hJob);
}
Declarations =>
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string lpName);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool SetInformationJobObject(IntPtr hJob, JOBOBJECTINFOCLASS JobObjectInfoClass, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool QueryInformationJobObject(IntPtr hJob, JOBOBJECTINFOCLASS JobObjectInformationClass, [Out, MarshalAs(UnmanagedType.SysUInt)] IntPtr lpJobObjectInformation, int cbJobObjectInformationLength, out int lpReturnLength);
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public ulong PerProcessUserTimeLimit;
public ulong PerJobUserTimeLimit;
public int LimitFlags;
public IntPtr MinimumWorkingSetSize;
public IntPtr MaximumWorkingSetSize;
public int ActiveProcessLimit;
public IntPtr Affinity;
public int PriorityClass;
public int SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
struct IO_COUNTERS
{
public ulong ReadOperationCount;
public ulong WriteOperationCount;
public ulong OtherOperationCount;
public ulong ReadTransferCount;
public ulong WriteTransferCount;
public ulong OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public IntPtr ProcessMemoryLimit;
public IntPtr JobMemoryLimit;
public IntPtr PeakProcessMemoryUsed;
public IntPtr PeakJobMemoryUsed;
}
//
// Basic Limits
//
public const int JOB_OBJECT_LIMIT_WORKINGSET = 0x00000001;
public const int JOB_OBJECT_LIMIT_PROCESS_TIME = 0x00000002;
public const int JOB_OBJECT_LIMIT_JOB_TIME = 0x00000004;
public const int JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 0x00000008;
public const int JOB_OBJECT_LIMIT_AFFINITY = 0x00000010;
public const int JOB_OBJECT_LIMIT_PRIORITY_CLASS = 0x00000020;
public const int JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME = 0x00000040;
public const int JOB_OBJECT_LIMIT_SCHEDULING_CLASS = 0x00000080;
//
// Extended Limits
//
public const int JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x00000100;
public const int JOB_OBJECT_LIMIT_JOB_MEMORY = 0x00000200;
public const int JOB_OBJECT_LIMIT_JOB_MEMORY_HIGH = JOB_OBJECT_LIMIT_JOB_MEMORY;
public const int JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x00000400;
public const int JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800;
public const int JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000;
public const int JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000;
public const int JOB_OBJECT_LIMIT_SUBSET_AFFINITY = 0x00004000;
public const int JOB_OBJECT_LIMIT_JOB_MEMORY_LOW = 0x00008000;
public enum JOBOBJECTINFOCLASS
{
JobObjectBasicAccountingInformation = 1,
JobObjectBasicLimitInformation,
JobObjectBasicProcessIdList,
JobObjectBasicUIRestrictions,
JobObjectSecurityLimitInformation, // deprecated
JobObjectEndOfJobTimeInformation,
JobObjectAssociateCompletionPortInformation,
JobObjectBasicAndIoAccountingInformation,
JobObjectExtendedLimitInformation,
JobObjectJobSetInformation,
JobObjectGroupInformation,
JobObjectNotificationLimitInformation,
JobObjectLimitViolationInformation,
JobObjectGroupInformationEx,
JobObjectCpuRateControlInformation,
JobObjectCompletionFilter,
JobObjectCompletionCounter,
JobObjectReserved1Information = 18,
JobObjectReserved2Information,
JobObjectReserved3Information,
JobObjectReserved4Information,
JobObjectReserved5Information,
JobObjectReserved6Information,
JobObjectReserved7Information,
JobObjectReserved8Information,
JobObjectReserved9Information,
JobObjectReserved10Information,
JobObjectReserved11Information,
JobObjectReserved12Information,
JobObjectReserved13Information,
JobObjectReserved14Information = 31,
JobObjectNetRateControlInformation,
JobObjectNotificationLimitInformation2,
JobObjectLimitViolationInformation2,
JobObjectCreateSilo,
JobObjectSiloBasicInformation,
JobObjectReserved15Information = 37,
JobObjectReserved16Information,
JobObjectReserved17Information,
JobObjectReserved18Information,
JobObjectReserved19Information = 41,
JobObjectReserved20Information,
MaxJobObjectInfoClass
}
I think any of the ways you mentioned should work.
In addition, there is a matching feature in c#: Define an array with the fixed keyword:
struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
int NumberOfAssignedProcesses;
int NumberOfProcessIdsInList;
fixed IntPtr ProcessIdList[1];
}
See documentation:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/unsafe-code-pointers/fixed-size-buffers
Also no bounds check, so you should be able to read behind the end of the struckt easily:
Note
Except for memory created by using stackalloc, the C# compiler and the common language runtime (CLR) do not perform any security buffer overrun checks. As with all unsafe code, use caution.
I am trying to connect to a USB device that is detected as a CDC device on my system using the following code :
(I have presented only the part of the code relevant to my problem)
The code compiles fine and SetupDiGetClassDevs function runs ok too.
But SetupDiEnumDeviceInterfaces always returns false and because of this I am not able to move any further. Please tell me where it is wrong. Thanks.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Runtime.InteropServices;
using System.IO.Ports;
//**************************************************************
// Static class for access to USB dll for all the activity
// related to USB device
//**************************************************************
namespace USBDeviceConnect
{
#region Unmanaged
public class Native
{
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetupDiGetClassDevs(
ref Guid ClassGuid,
IntPtr Enumerator,
IntPtr hwndParent,
UInt32 Flags);
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
public static extern bool SetupDiEnumDeviceInterfaces(
IntPtr hDevInfo,
SP_DEVINFO_DATA devInfo,
ref Guid interfaceClassGuid,
UInt32 memberIndex,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);
[DllImport(#"setupapi.dll", CharSet = CharSet.Auto)]
public static extern Boolean SetupDiEnumDeviceInterfaces(
IntPtr hDevInfo,
IntPtr devInfo,
ref Guid interfaceClassGuid,
UInt32 memberIndex,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
public static extern int SetupDiDestroyDeviceInfoList(
IntPtr DeviceInfoSet);
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
public static extern bool SetupDiEnumDeviceInfo(
IntPtr DeviceInfoSet,
UInt32 MemberIndex,
ref SP_DEVINFO_DATA DeviceInfoData);
//SP_DEVINFO_DATA
[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVINFO_DATA
{
public int cbSize;
public Guid ClassGuid;
public int DevInst;
public ulong Reserved;
}
[StructLayout(LayoutKind.Sequential)]
public class SP_DEVICE_INTERFACE_DATA
{
public int cbSize;
public Guid interfaceClassGuid;
public int flags;
public ulong reserved;
};
//PARMS
public const int DIGCF_ALLCLASSES = (0x00000004);
public const int DIGCF_PRESENT = (0x00000002);
public const int DIGCF_PROFILE = (0x00000008);
public const int DIGCF_DEVICEINTERFACE = (0x00000010);
public const int INVALID_HANDLE_VALUE = -1;
public const int MAX_DEV_LEN = 1000;
public const int DEVICE_NOTIFY_WINDOW_HANDLE = (0x00000000);
public const int DEVICE_NOTIFY_SERVICE_HANDLE = (0x00000001);
public const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = (0x00000004);
public const int DBT_DEVTYP_DEVICEINTERFACE = (0x00000005);
public const int DBT_DEVNODES_CHANGED = (0x0007);
public const int WM_DEVICECHANGE = (0x0219);
public const int DIF_PROPERTYCHANGE = (0x00000012);
public const int DICS_FLAG_GLOBAL = (0x00000001);
public const int DICS_FLAG_CONFIGSPECIFIC = (0x00000002);
public const int DICS_ENABLE = (0x00000001);
public const int DICS_DISABLE = (0x00000002);
public const long ERROR_NO_MORE_ITEMS = 259L;
public const int ERROR_SUCCESS = 0;
}
#endregion
/****************************************************************************/
static class AccessUSB
{
public static void Main()
{
ConnectToDevice();
}
/* Execution starts here */
public static void ConnectToDevice()
{
/* Get the info of all connected devices */
//Globally Unique Identifier (GUID) for CDC class devices.
Guid InterfaceClassGuid = new Guid("a5dcbf10653011d2901f00c04fb951ed");
IntPtr DeviceInfoTable = (IntPtr)Native.INVALID_HANDLE_VALUE;
Native.SP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new Native.SP_DEVICE_INTERFACE_DATA();
Native.SP_DEVINFO_DATA DevInfoData = new Native.SP_DEVINFO_DATA();
uint InterfaceIndex = 0;
uint ErrorStatus;
StringBuilder DeviceName = new StringBuilder();
DeviceInfoTable = Native.SetupDiGetClassDevs(ref InterfaceClassGuid, IntPtr.Zero, IntPtr.Zero, Native.DIGCF_PRESENT | Native.DIGCF_DEVICEINTERFACE);
if (DeviceInfoTable.ToInt32() != Native.INVALID_HANDLE_VALUE)
{
//Initialize an appropriate SP_DEVINFO_DATA structure.
DevInfoData.cbSize = Marshal.SizeOf(DevInfoData);
while (true)
{
InterfaceDataStructure.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(InterfaceDataStructure);
bool bRslt = Native.SetupDiEnumDeviceInterfaces(DeviceInfoTable, IntPtr.Zero, ref InterfaceClassGuid, InterfaceIndex, ref InterfaceDataStructure);
if (bRslt == true)
{
ErrorStatus = (uint)System.Runtime.InteropServices.Marshal.GetLastWin32Error();
if (Native.ERROR_NO_MORE_ITEMS == ErrorStatus)
{
Native.SetupDiDestroyDeviceInfoList(DeviceInfoTable);//Clean up the old structure we no longer need.
return;
}
}
else //Else some other kind of unknown error ocurred...
{
ErrorStatus = (uint)System.Runtime.InteropServices.Marshal.GetLastWin32Error();
Native.SetupDiDestroyDeviceInfoList(DeviceInfoTable); //Clean up the old structure we no longer need.
return;
}
InterfaceIndex++;
}//end of while(true)
}
}
}
}
I'm not sure why you are trying to use SetupAPI. If it is a detected as a CDC ACM device, then it should have the usbser.sys driver associated with it, and it should show a COM port number in the Device Manager, and you can connect to that COM port using the .NET SerialPort class:
http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.aspx
there are some similar questions to this on the net - even a few here, but even though the askers seem happy I can't find one that actually does what I need.
I'm trying to add a remote directory browsing feature to a web-based administration control panel (intranet based).
I don't need to worry about security at this point as this is handled elsewhere.
To do this I'm using a webservice which accepts a server name and a share/folder path as parameters. I just need it to return the subdirectories of this path, if any.
Doesn't sound so hard, does it? Well, it is (at least to me!)
The only bit I need help with is actually producing a list of directories for the server and path supplied.
All help is appreciated, but please don't just link to a site as I've probably seen it already but failed to get a working solution; most of these don't even seem to attempt to do what the title implies.
Some explaination would be helpful as well!
Cheers
To enumerate subfolders of specified folder in .NET you can use for example DirectoryInfo.EnumerateDirectories method.
To enumerate shares of some computer you can use WNetEnumResource native function if hidden administrative shares like C$, ADMIN$, print$ and so on are not important for you or use NetShareEnum to enumerate all shares.
The corresponding code could be
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Runtime.InteropServices;
namespace Subfolders {
static internal class Native {
[DllImport ("Netapi32.dll", SetLastError = true)]
internal static extern uint NetApiBufferFree (IntPtr buffer);
[DllImport ("Netapi32.dll", CharSet = CharSet.Unicode)]
internal static extern uint NetShareEnum (
string serverName,
int level,
ref IntPtr bufPtr,
uint prefmaxlen,
ref int entriesread,
ref int totalentries,
ref int resumeHandle
);
[DllImport ("MPR.dll", CharSet = CharSet.Auto)]
internal static extern uint WNetEnumResource(IntPtr hEnum, ref int lpcCount, IntPtr lpBuffer, ref int lpBufferSize);
[DllImport ("MPR.dll", CharSet = CharSet.Auto)]
internal static extern uint WNetOpenEnum(ResourceScope dwScope, ResourceType dwType, ResourceUsage dwUsage,
IntPtr lpNetResource, out IntPtr lphEnum);
[DllImport ("MPR.dll", CharSet = CharSet.Auto)]
internal static extern uint WNetCloseEnum(IntPtr hEnum);
internal const uint MaxPreferredLength = 0xFFFFFFFF;
internal const int NerrSuccess = 0;
internal enum NetError : uint {
NerrSuccess = 0,
NerrBase = 2100,
NerrUnknownDevDir = (NerrBase + 16),
NerrDuplicateShare = (NerrBase + 18),
NerrBufTooSmall = (NerrBase + 23),
}
internal enum ShareType : uint {
StypeDisktree = 0,
StypePrintq = 1,
StypeDevice = 2,
StypeIpc = 3,
StypeSpecial = 0x80000000,
}
[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct ShareInfo1 {
public string shi1_netname;
public uint shi1_type;
public string shi1_remark;
public ShareInfo1 (string sharename, uint sharetype, string remark) {
shi1_netname = sharename;
shi1_type = sharetype;
shi1_remark = remark;
}
public override string ToString () {
return shi1_netname;
}
}
public enum ResourceScope: uint {
ResourceConnected = 0x00000001,
ResourceGlobalnet = 0x00000002,
ResourceRemembered = 0x00000003,
ResourceRecent = 0x00000004,
ResourceContext = 0x00000005
}
public enum ResourceType: uint {
ResourcetypeAny = 0x00000000,
ResourcetypeDisk = 0x00000001,
ResourcetypePrint = 0x00000002,
ResourcetypeReserved = 0x00000008,
ResourcetypeUnknown = 0xFFFFFFFF
}
public enum ResourceUsage: uint {
ResourceusageConnectable = 0x00000001,
ResourceusageContainer = 0x00000002,
ResourceusageNolocaldevice = 0x00000004,
ResourceusageSibling = 0x00000008,
ResourceusageAttached = 0x00000010,
ResourceusageAll = (ResourceusageConnectable | ResourceusageContainer | ResourceusageAttached),
ResourceusageReserved = 0x80000000
}
public enum ResourceDisplaytype: uint {
ResourcedisplaytypeGeneric = 0x00000000,
ResourcedisplaytypeDomain = 0x00000001,
ResourcedisplaytypeServer = 0x00000002,
ResourcedisplaytypeShare = 0x00000003,
ResourcedisplaytypeFile = 0x00000004,
ResourcedisplaytypeGroup = 0x00000005,
ResourcedisplaytypeNetwork = 0x00000006,
ResourcedisplaytypeRoot = 0x00000007,
ResourcedisplaytypeShareadmin = 0x00000008,
ResourcedisplaytypeDirectory = 0x00000009,
ResourcedisplaytypeTree = 0x0000000A,
ResourcedisplaytypeNdscontainer = 0x0000000B
}
[StructLayout (LayoutKind.Sequential)]
public struct NetResource {
public ResourceScope dwScope;
public ResourceType dwType;
public ResourceDisplaytype dwDisplayType;
public ResourceUsage dwUsage;
[MarshalAs (UnmanagedType.LPTStr)]
public string lpLocalName;
[MarshalAs (UnmanagedType.LPTStr)]
public string lpRemoteName;
[MarshalAs (UnmanagedType.LPTStr)]
public string lpComment;
[MarshalAs (UnmanagedType.LPTStr)]
public string lpProvider;
}
}
class Program {
static IEnumerable<string> GetShares(string computerName) {
var resources = new List<string>();
IntPtr hEnum = IntPtr.Zero, pResource = IntPtr.Zero;
try {
var resource = new Native.NetResource();
int bufferSize = 163840;
resource.dwType = Native.ResourceType.ResourcetypeAny;
resource.dwScope = Native.ResourceScope.ResourceGlobalnet;
resource.dwUsage = Native.ResourceUsage.ResourceusageContainer;
resource.lpRemoteName = computerName;
pResource = Marshal.AllocHGlobal(Marshal.SizeOf(resource));
Marshal.StructureToPtr (resource, pResource, false);
uint status = Native.WNetOpenEnum (Native.ResourceScope.ResourceGlobalnet,
Native.ResourceType.ResourcetypeDisk,
0,
pResource,
out hEnum);
if (status != 0)
return resources;
int numberOfEntries = -1;
IntPtr pBuffer = Marshal.AllocHGlobal(bufferSize);
status = Native.WNetEnumResource (hEnum, ref numberOfEntries, pBuffer, ref bufferSize);
if (status == Native.NerrSuccess && numberOfEntries > 0) {
var ptr = pBuffer;
for (int i = 0; i < numberOfEntries; i++, ptr += Marshal.SizeOf(resource)) {
resource = (Native.NetResource)Marshal.PtrToStructure (ptr, typeof (Native.NetResource));
resources.Add (resource.lpRemoteName.StartsWith (computerName + '\\',
StringComparison.OrdinalIgnoreCase)
? resource.lpRemoteName.Substring (computerName.Length + 1)
: resource.lpRemoteName);
}
}
} finally {
if (hEnum != IntPtr.Zero) {
Native.WNetCloseEnum (hEnum);
}
if (pResource != IntPtr.Zero) {
Marshal.FreeHGlobal(pResource);
}
}
return resources;
}
static IEnumerable<string> GetAllShares (string computerName) {
var shares = new List<string> ();
IntPtr bufPtr = IntPtr.Zero;
int entriesread = 0;
int totalentries = 0;
int resumeHandle = 0;
int nStructSize = Marshal.SizeOf (typeof (Native.ShareInfo1));
try {
uint ret = Native.NetShareEnum (computerName, 1, ref bufPtr,
Native.MaxPreferredLength,
ref entriesread,
ref totalentries,
ref resumeHandle);
if (ret == (uint)Native.NetError.NerrSuccess) {
var currentPtr = bufPtr;
for (int i = 0; i < entriesread; i++) {
var shi1 = (Native.ShareInfo1)Marshal.PtrToStructure (currentPtr, typeof (Native.ShareInfo1));
if ((shi1.shi1_type & ~(uint)Native.ShareType.StypeSpecial) == (uint)Native.ShareType.StypeDisktree) {
shares.Add (shi1.shi1_netname);
}
currentPtr = new IntPtr (currentPtr.ToInt32 () + nStructSize);
}
}
} finally {
if (bufPtr != IntPtr.Zero)
Native.NetApiBufferFree (bufPtr);
}
return shares;
}
static IEnumerable<string> GetSubdirectories (string root) {
var dirInfo = new DirectoryInfo (root);
return (from info in dirInfo.EnumerateDirectories () select info.Name).ToList();
}
static void Main () {
var root = #"\\OK01\Users";
Console.WriteLine ("Subdirectories of {0}:", root);
foreach (var dir in GetSubdirectories (root)) {
Console.WriteLine (dir);
}
Console.WriteLine ();
root = #"\\OK01\Users\Public";
Console.WriteLine ("Subdirectories of {0}:", root);
foreach (var dir in GetSubdirectories (root)) {
Console.WriteLine (dir);
}
Console.WriteLine ();
root = #"\\OK01";
Console.WriteLine ("All Shares of {0} (inclusive hidden):", root);
foreach (var shareName in GetAllShares (root)) {
Console.WriteLine (shareName);
}
Console.WriteLine ();
root = #"\\OK01";
Console.WriteLine ("Shares of {0}:", root);
foreach (var shareName in GetShares (root)) {
Console.WriteLine (shareName);
}
}
}
}
which produce output like
Subdirectories of \\OK01\Users:
All Users
ASP.NET v4.0
Default
Default User
MSSQL$SQL2012
Oleg
Public
Subdirectories of \\OK01\Users\Public:
Desktop
Documents
Downloads
Favorites
Libraries
Music
Pictures
Recorded TV
Roaming
Videos
All Shares of \\OK01 (inclusive hidden):
ADMIN$
C$
print$
Q$
Users
Virtual Machines
VMware
Shares of \\OK01:
Users
Virtual Machines
VMware
The above code is simplified to demonstrate only how to use the corresponding API. It contains no real error reporting.
You can use method described here using Interop.
I made a few modifications to the code to come up with this. I have not extensively tested this, so it may have errors but it should get you started.
private List<string> GetSubDirectories(string serverName, string folderPath)
{
List<string> subDirectories = new List<string>();
string folder_path = Path.Combine(serverName, folderPath);
IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
WIN32_FIND_DATA findData;
IntPtr findHandle;
findHandle = FindFirstFile(folder_path, out findData);
if (findHandle == INVALID_HANDLE_VALUE)
{
int error = Marshal.GetLastWin32Error();
Console.WriteLine(error.ToString());
return null;
}
do
{
try
{
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
subDirectories.Add(findData.cFileName);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
while (FindNextFile(findHandle, out findData));
FindClose(findHandle);
return subDirectories;
}
public const int FILE_ATTRIBUTE_DIRECTORY = 0x10;
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool FindClose(IntPtr hFindFile);
[StructLayout(LayoutKind.Sequential)]
public struct FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
};
[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", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
}
You can call it like:
var subdirectories = GetSubDirectories(#"\\[serverName]", #"[folderPath]\*");
You have to add "\*" as per MSDN
On network shares, you can use an lpFileName in the form of the
following: "\Server\Share*". However, you cannot use an lpFileName
that points to the share itself; for example, "\Server\Share" is not
valid.
Not sure if we can achieve this. We had similar problem but finally resolved it by providing the shared path (\SERVERNAME\FOLDER).
Most important webservice should use a account that has full permission to access the directory else exception related to permission will be thrown out to calling client.
Well, actually it can be done using NetShareEnum Win32API function.
But here is .NET wrapper classes to enumerate network shares on local and remote machines, and convert local file paths to UNC paths. Please see the article Network Shares and UNC paths.
In my application I need to verify whether it's signed or not. If it's signed continue the execution and exit the application if not. The signtool will be used to sign the application.
Is there any C# code to do that?
Here is a utility method that does it:
var signed = IsSigned(#"c:\windows\explorer.exe");
...
public static bool IsSigned(string filePath)
{
if (filePath == null)
throw new ArgumentNullException(nameof(filePath));
var file = new WINTRUST_FILE_INFO();
file.cbStruct = Marshal.SizeOf(typeof(WINTRUST_FILE_INFO));
file.pcwszFilePath = filePath;
var data = new WINTRUST_DATA();
data.cbStruct = Marshal.SizeOf(typeof(WINTRUST_DATA));
data.dwUIChoice = WTD_UI_NONE;
data.dwUnionChoice = WTD_CHOICE_FILE;
data.fdwRevocationChecks = WTD_REVOKE_NONE;
data.pFile = Marshal.AllocHGlobal(file.cbStruct);
Marshal.StructureToPtr(file, data.pFile, false);
int hr;
try
{
hr = WinVerifyTrust(INVALID_HANDLE_VALUE, WINTRUST_ACTION_GENERIC_VERIFY_V2, ref data);
}
finally
{
Marshal.FreeHGlobal(data.pFile);
}
return hr == 0;
}
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct WINTRUST_FILE_INFO
{
public int cbStruct;
public string pcwszFilePath;
public IntPtr hFile;
public IntPtr pgKnownSubject;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
private struct WINTRUST_DATA
{
public int cbStruct;
public IntPtr pPolicyCallbackData;
public IntPtr pSIPClientData;
public int dwUIChoice;
public int fdwRevocationChecks;
public int dwUnionChoice;
public IntPtr pFile;
public int dwStateAction;
public IntPtr hWVTStateData;
public IntPtr pwszURLReference;
public int dwProvFlags;
public int dwUIContext;
public IntPtr pSignatureSettings;
}
private const int WTD_UI_NONE = 2;
private const int WTD_REVOKE_NONE = 0;
private const int WTD_CHOICE_FILE = 1;
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
private static readonly Guid WINTRUST_ACTION_GENERIC_VERIFY_V2 = new Guid("{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}");
[DllImport("wintrust.dll")]
private static extern int WinVerifyTrust(IntPtr hwnd, [MarshalAs(UnmanagedType.LPStruct)] Guid pgActionID, ref WINTRUST_DATA pWVTData);
Try
Dim objCertificate As New Security.Cryptography.X509Certificates.X509Certificate2(Security.Cryptography.X509Certificates.X509Certificate.CreateFromSignedFile(strFile))
Return True
Catch ex As Exception
Return False
End Try
private bool IsAssemblySigned()
{
var assembly = Assembly.GetAssembly(GetType());
var assemblyName = assembly.GetName();
var key = assemblyName.GetPublicKey();
return key.Length > 0;
}
I would suggest you use the 'CryptUIWizDigitalSign' API. This link can be used as a reference.
We need to verify that binary files are signed properly with digital signature (Authenticode). This can be achieved with signtool.exe pretty easily. However, we need an automatic way that also verifies signer name and timestamp. This is doable in native C++ with CryptQueryObject() API as shown in this wonderful sample: How To Get Information from Authenticode Signed Executables
However we live in a managed world :) hence looking for C# solution to the same problem. Straight approach would be to pInvoke Crypt32.dll and all is done. But there is similar managed API in System.Security.Cryptography.X509Certificates Namespace. X509Certificate2 Class seems to provide some information but no timestamp. Now we came to the original question how can we get that timestamp of a digital signature in C Sharp?
Back to the original question, I could not find managed way so ended up using pInvoke as follows:
public static bool IsTimestamped(string filename)
{
try
{
int encodingType;
int contentType;
int formatType;
IntPtr certStore = IntPtr.Zero;
IntPtr cryptMsg = IntPtr.Zero;
IntPtr context = IntPtr.Zero;
if (!WinCrypt.CryptQueryObject(
WinCrypt.CERT_QUERY_OBJECT_FILE,
Marshal.StringToHGlobalUni(filename),
WinCrypt.CERT_QUERY_CONTENT_FLAG_ALL,
WinCrypt.CERT_QUERY_FORMAT_FLAG_ALL,
0,
out encodingType,
out contentType,
out formatType,
ref certStore,
ref cryptMsg,
ref context))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
//expecting contentType=10; CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED
//Logger.LogInfo(string.Format("Querying file '{0}':", filename));
//Logger.LogInfo(string.Format(" Encoding Type: {0}", encodingType));
//Logger.LogInfo(string.Format(" Content Type: {0}", contentType));
//Logger.LogInfo(string.Format(" Format Type: {0}", formatType));
//Logger.LogInfo(string.Format(" Cert Store: {0}", certStore.ToInt32()));
//Logger.LogInfo(string.Format(" Crypt Msg: {0}", cryptMsg.ToInt32()));
//Logger.LogInfo(string.Format(" Context: {0}", context.ToInt32()));
// Get size of the encoded message.
int cbData = 0;
if (!WinCrypt.CryptMsgGetParam(
cryptMsg,
WinCrypt.CMSG_ENCODED_MESSAGE,//Crypt32.CMSG_SIGNER_INFO_PARAM,
0,
IntPtr.Zero,
ref cbData))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var vData = new byte[cbData];
// Get the encoded message.
if (!WinCrypt.CryptMsgGetParam(
cryptMsg,
WinCrypt.CMSG_ENCODED_MESSAGE,//Crypt32.CMSG_SIGNER_INFO_PARAM,
0,
vData,
ref cbData))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var signedCms = new SignedCms();
signedCms.Decode(vData);
foreach (var signerInfo in signedCms.SignerInfos)
{
foreach (var unsignedAttribute in signerInfo.UnsignedAttributes)
{
if (unsignedAttribute.Oid.Value == WinCrypt.szOID_RSA_counterSign)
{
//Note at this point we assume this counter signature is the timestamp
//refer to http://support.microsoft.com/kb/323809 for the origins
//TODO: extract timestamp value, if required
return true;
}
}
}
}
catch (Exception)
{
// no logging
}
return false;
}
and the WinCrypt.cs contains the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace MyNamespace.Win32
{
static class WinCrypt
{
[StructLayout(LayoutKind.Sequential)]
public struct BLOB
{
public int cbData;
public IntPtr pbData;
}
[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_ALGORITHM_IDENTIFIER
{
public String pszObjId;
BLOB Parameters;
}
[StructLayout(LayoutKind.Sequential)]
public struct CERT_ID
{
public int dwIdChoice;
public BLOB IssuerSerialNumberOrKeyIdOrHashId;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct SIGNER_SUBJECT_INFO
{
/// DWORD->unsigned int
public uint cbSize;
/// DWORD*
public System.IntPtr pdwIndex;
/// DWORD->unsigned int
public uint dwSubjectChoice;
/// SubjectChoiceUnion
public SubjectChoiceUnion Union1;
}
[StructLayoutAttribute(LayoutKind.Explicit)]
public struct SubjectChoiceUnion
{
/// SIGNER_FILE_INFO*
[FieldOffsetAttribute(0)]
public System.IntPtr pSignerFileInfo;
/// SIGNER_BLOB_INFO*
[FieldOffsetAttribute(0)]
public System.IntPtr pSignerBlobInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct CERT_NAME_BLOB
{
public uint cbData;
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
public byte[] pbData;
}
[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_INTEGER_BLOB
{
public UInt32 cbData;
public IntPtr pbData;
}
[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_ATTR_BLOB
{
public uint cbData;
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
public byte[] pbData;
}
[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_ATTRIBUTE
{
[MarshalAs(UnmanagedType.LPStr)]
public string pszObjId;
public uint cValue;
[MarshalAs(UnmanagedType.LPStruct)]
public CRYPT_ATTR_BLOB rgValue;
}
[StructLayout(LayoutKind.Sequential)]
public struct CMSG_SIGNER_INFO
{
public int dwVersion;
private CERT_NAME_BLOB Issuer;
CRYPT_INTEGER_BLOB SerialNumber;
CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;
BLOB EncryptedHash;
CRYPT_ATTRIBUTE[] AuthAttrs;
CRYPT_ATTRIBUTE[] UnauthAttrs;
}
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CryptQueryObject(
int dwObjectType,
IntPtr pvObject,
int dwExpectedContentTypeFlags,
int dwExpectedFormatTypeFlags,
int dwFlags,
out int pdwMsgAndCertEncodingType,
out int pdwContentType,
out int pdwFormatType,
ref IntPtr phCertStore,
ref IntPtr phMsg,
ref IntPtr ppvContext);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CryptMsgGetParam(
IntPtr hCryptMsg,
int dwParamType,
int dwIndex,
IntPtr pvData,
ref int pcbData
);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CryptMsgGetParam(
IntPtr hCryptMsg,
int dwParamType,
int dwIndex,
[In, Out] byte[] vData,
ref int pcbData
);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptDecodeObject(
uint CertEncodingType,
UIntPtr lpszStructType,
byte[] pbEncoded,
uint cbEncoded,
uint flags,
[In, Out] byte[] pvStructInfo,
ref uint cbStructInfo);
public const int CRYPT_ASN_ENCODING = 0x00000001;
public const int CRYPT_NDR_ENCODING = 0x00000002;
public const int X509_ASN_ENCODING = 0x00000001;
public const int X509_NDR_ENCODING = 0x00000002;
public const int PKCS_7_ASN_ENCODING = 0x00010000;
public const int PKCS_7_NDR_ENCODING = 0x00020000;
public static UIntPtr PKCS7_SIGNER_INFO = new UIntPtr(500);
public static UIntPtr CMS_SIGNER_INFO = new UIntPtr(501);
public static string szOID_RSA_signingTime = "1.2.840.113549.1.9.5";
public static string szOID_RSA_counterSign = "1.2.840.113549.1.9.6";
//+-------------------------------------------------------------------------
// Get parameter types and their corresponding data structure definitions.
//--------------------------------------------------------------------------
public const int CMSG_TYPE_PARAM = 1;
public const int CMSG_CONTENT_PARAM = 2;
public const int CMSG_BARE_CONTENT_PARAM = 3;
public const int CMSG_INNER_CONTENT_TYPE_PARAM = 4;
public const int CMSG_SIGNER_COUNT_PARAM = 5;
public const int CMSG_SIGNER_INFO_PARAM = 6;
public const int CMSG_SIGNER_CERT_INFO_PARAM = 7;
public const int CMSG_SIGNER_HASH_ALGORITHM_PARAM = 8;
public const int CMSG_SIGNER_AUTH_ATTR_PARAM = 9;
public const int CMSG_SIGNER_UNAUTH_ATTR_PARAM = 10;
public const int CMSG_CERT_COUNT_PARAM = 11;
public const int CMSG_CERT_PARAM = 12;
public const int CMSG_CRL_COUNT_PARAM = 13;
public const int CMSG_CRL_PARAM = 14;
public const int CMSG_ENVELOPE_ALGORITHM_PARAM = 15;
public const int CMSG_RECIPIENT_COUNT_PARAM = 17;
public const int CMSG_RECIPIENT_INDEX_PARAM = 18;
public const int CMSG_RECIPIENT_INFO_PARAM = 19;
public const int CMSG_HASH_ALGORITHM_PARAM = 20;
public const int CMSG_HASH_DATA_PARAM = 21;
public const int CMSG_COMPUTED_HASH_PARAM = 22;
public const int CMSG_ENCRYPT_PARAM = 26;
public const int CMSG_ENCRYPTED_DIGEST = 27;
public const int CMSG_ENCODED_SIGNER = 28;
public const int CMSG_ENCODED_MESSAGE = 29;
public const int CMSG_VERSION_PARAM = 30;
public const int CMSG_ATTR_CERT_COUNT_PARAM = 31;
public const int CMSG_ATTR_CERT_PARAM = 32;
public const int CMSG_CMS_RECIPIENT_COUNT_PARAM = 33;
public const int CMSG_CMS_RECIPIENT_INDEX_PARAM = 34;
public const int CMSG_CMS_RECIPIENT_ENCRYPTED_KEY_INDEX_PARAM = 35;
public const int CMSG_CMS_RECIPIENT_INFO_PARAM = 36;
public const int CMSG_UNPROTECTED_ATTR_PARAM = 37;
public const int CMSG_SIGNER_CERT_ID_PARAM = 38;
public const int CMSG_CMS_SIGNER_INFO_PARAM = 39;
//-------------------------------------------------------------------------
//dwObjectType for CryptQueryObject
//-------------------------------------------------------------------------
public const int CERT_QUERY_OBJECT_FILE = 0x00000001;
public const int CERT_QUERY_OBJECT_BLOB = 0x00000002;
//-------------------------------------------------------------------------
//dwContentType for CryptQueryObject
//-------------------------------------------------------------------------
//encoded single certificate
public const int CERT_QUERY_CONTENT_CERT = 1;
//encoded single CTL
public const int CERT_QUERY_CONTENT_CTL = 2;
//encoded single CRL
public const int CERT_QUERY_CONTENT_CRL = 3;
//serialized store
public const int CERT_QUERY_CONTENT_SERIALIZED_STORE = 4;
//serialized single certificate
public const int CERT_QUERY_CONTENT_SERIALIZED_CERT = 5;
//serialized single CTL
public const int CERT_QUERY_CONTENT_SERIALIZED_CTL = 6;
//serialized single CRL
public const int CERT_QUERY_CONTENT_SERIALIZED_CRL = 7;
//a PKCS#7 signed message
public const int CERT_QUERY_CONTENT_PKCS7_SIGNED = 8;
//a PKCS#7 message, such as enveloped message. But it is not a signed message,
public const int CERT_QUERY_CONTENT_PKCS7_UNSIGNED = 9;
//a PKCS7 signed message embedded in a file
public const int CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED = 10;
//an encoded PKCS#10
public const int CERT_QUERY_CONTENT_PKCS10 = 11;
//an encoded PKX BLOB
public const int CERT_QUERY_CONTENT_PFX = 12;
//an encoded CertificatePair (contains forward and/or reverse cross certs)
public const int CERT_QUERY_CONTENT_CERT_PAIR = 13;
//-------------------------------------------------------------------------
//dwExpectedConentTypeFlags for CryptQueryObject
//-------------------------------------------------------------------------
//encoded single certificate
public const int CERT_QUERY_CONTENT_FLAG_CERT = (1 << CERT_QUERY_CONTENT_CERT);
//encoded single CTL
public const int CERT_QUERY_CONTENT_FLAG_CTL = (1 << CERT_QUERY_CONTENT_CTL);
//encoded single CRL
public const int CERT_QUERY_CONTENT_FLAG_CRL = (1 << CERT_QUERY_CONTENT_CRL);
//serialized store
public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE = (1 << CERT_QUERY_CONTENT_SERIALIZED_STORE);
//serialized single certificate
public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT = (1 << CERT_QUERY_CONTENT_SERIALIZED_CERT);
//serialized single CTL
public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL = (1 << CERT_QUERY_CONTENT_SERIALIZED_CTL);
//serialized single CRL
public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL = (1 << CERT_QUERY_CONTENT_SERIALIZED_CRL);
//an encoded PKCS#7 signed message
public const int CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED = (1 << CERT_QUERY_CONTENT_PKCS7_SIGNED);
//an encoded PKCS#7 message. But it is not a signed message
public const int CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED = (1 << CERT_QUERY_CONTENT_PKCS7_UNSIGNED);
//the content includes an embedded PKCS7 signed message
public const int CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED = (1 << CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED);
//an encoded PKCS#10
public const int CERT_QUERY_CONTENT_FLAG_PKCS10 = (1 << CERT_QUERY_CONTENT_PKCS10);
//an encoded PFX BLOB
public const int CERT_QUERY_CONTENT_FLAG_PFX = (1 << CERT_QUERY_CONTENT_PFX);
//an encoded CertificatePair (contains forward and/or reverse cross certs)
public const int CERT_QUERY_CONTENT_FLAG_CERT_PAIR = (1 << CERT_QUERY_CONTENT_CERT_PAIR);
//content can be any type
public const int CERT_QUERY_CONTENT_FLAG_ALL =
CERT_QUERY_CONTENT_FLAG_CERT |
CERT_QUERY_CONTENT_FLAG_CTL |
CERT_QUERY_CONTENT_FLAG_CRL |
CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT |
CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL |
CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL |
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
CERT_QUERY_CONTENT_FLAG_PKCS10 |
CERT_QUERY_CONTENT_FLAG_PFX |
CERT_QUERY_CONTENT_FLAG_CERT_PAIR;
//-------------------------------------------------------------------------
//dwFormatType for CryptQueryObject
//-------------------------------------------------------------------------
//the content is in binary format
public const int CERT_QUERY_FORMAT_BINARY = 1;
//the content is base64 encoded
public const int CERT_QUERY_FORMAT_BASE64_ENCODED = 2;
//the content is ascii hex encoded with "{ASN}" prefix
public const int CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED = 3;
//-------------------------------------------------------------------------
//dwExpectedFormatTypeFlags for CryptQueryObject
//-------------------------------------------------------------------------
//the content is in binary format
public const int CERT_QUERY_FORMAT_FLAG_BINARY = (1 << CERT_QUERY_FORMAT_BINARY);
//the content is base64 encoded
public const int CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED = (1 << CERT_QUERY_FORMAT_BASE64_ENCODED);
//the content is ascii hex encoded with "{ASN}" prefix
public const int CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED = (1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED);
//the content can be of any format
public const int CERT_QUERY_FORMAT_FLAG_ALL =
CERT_QUERY_FORMAT_FLAG_BINARY |
CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED |
CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED;
}
}
Thank you guys,
You help me a lot :)
BTW: I found simpler way how to obtain the time stamp.
here it is:
foreach (var signerInfo in signedCms.SignerInfos)
{
foreach (var unsignedAttribute in signerInfo.UnsignedAttributes)
{
if (unsignedAttribute.Oid.Value == WinCrypt.szOID_RSA_counterSign)
{
foreach (var counterSignInfo in signerInfo.CounterSignerInfos)
{
foreach (var signedAttribute in counterSignInfo.SignedAttributes)
{
if (signedAttribute.Oid.Value == WinCrypt.szOID_RSA_signingTime)
{
Pkcs9SigningTime signingTime = (Pkcs9SigningTime)signedAttribute.Values[0];
Console.Out.WriteLine("Signing Time UTC: " + signingTime.SigningTime);
}
}
}
return true;
}
}
}
Thanks to the OP for your work. I added the implementation to get the actual TimeStamp of the cert.
foreach (var signerInfo in signedCms.SignerInfos)
{
foreach (var unsignedAttribute in signerInfo.UnsignedAttributes)
{
if (unsignedAttribute.Oid.Value == WinCrypt.szOID_RSA_counterSign)
{
foreach (var counterSignInfo in signerInfo.CounterSignerInfos)
{
foreach (var signedAttribute in counterSignInfo.SignedAttributes)
{
if (signedAttribute.Oid.Value == WinCrypt.szOID_RSA_signingTime)
{
System.Runtime.InteropServices.ComTypes.FILETIME fileTime = new System.Runtime.InteropServices.ComTypes.FILETIME();
int fileTimeSize = Marshal.SizeOf(fileTime);
IntPtr fileTimePtr = Marshal.AllocCoTaskMem(fileTimeSize);
Marshal.StructureToPtr(fileTime, fileTimePtr, true);
byte[] buffdata = new byte[fileTimeSize];
Marshal.Copy(fileTimePtr, buffdata, 0, fileTimeSize);
uint buffSize = (uint)buffdata.Length;
uint encoding = WinCrypt.X509_ASN_ENCODING | WinCrypt.PKCS_7_ASN_ENCODING;
UIntPtr rsaSigningTime = (UIntPtr)(uint)Marshal.StringToHGlobalAnsi(WinCrypt.szOID_RSA_signingTime);
byte[] pbData = signedAttribute.Values[0].RawData;
uint ucbData = (uint)pbData.Length;
bool workie = WinCrypt.CryptDecodeObject(encoding, rsaSigningTime.ToUInt32(), pbData, ucbData, 0, buffdata, ref buffSize);
if (workie)
{
IntPtr fileTimePtr2 = Marshal.AllocCoTaskMem(buffdata.Length);
Marshal.Copy(buffdata, 0, fileTimePtr2, buffdata.Length);
System.Runtime.InteropServices.ComTypes.FILETIME fileTime2 = (System.Runtime.InteropServices.ComTypes.FILETIME)Marshal.PtrToStructure(fileTimePtr2, typeof(System.Runtime.InteropServices.ComTypes.FILETIME));
long hFT2 = (((long)fileTime2.dwHighDateTime) << 32) + ((uint)fileTime2.dwLowDateTime);
DateTime dte = DateTime.FromFileTime(hFT2);
Console.WriteLine(dte.ToString());
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
return true;
}
}
}
I wanted to get the subject off the digital certificate, its an OU type string like
CN=Microsoft Corporation, OU=MOPR, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
I found the X509Certificate to be really slow and load the entire file into memory. I tried to read an 800Mb patch file and my memory increased by 800Mb as it read it, and it took over 30 seconds!!
I have stood on the sholders of the posters above an managed to tweak the above code to get an X509Certificate2 object hundreds of time faster than using the X509 Object.
Please read my blog post for more details with images of the performance differences. X509Certificate object c# performance and memory issues alternative – fixed
Try this:
public static X509Certificate2 GetDigitalCertificate(string filename)
{
X509Certificate2 cert = null;
int encodingType;
int contentType;
int formatType;
IntPtr certStore = IntPtr.Zero;
IntPtr cryptMsg = IntPtr.Zero;
IntPtr context = IntPtr.Zero;
if (!WinCrypt.CryptQueryObject(
WinCrypt.CERT_QUERY_OBJECT_FILE,
Marshal.StringToHGlobalUni(filename),
(WinCrypt.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED
| WinCrypt.CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED
| WinCrypt.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED), // <-- These are the attributes that makes it fast!!
WinCrypt.CERT_QUERY_FORMAT_FLAG_ALL,
0,
out encodingType,
out contentType,
out formatType,
ref certStore,
ref cryptMsg,
ref context))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
// Get size of the encoded message.
int cbData = 0;
if (!WinCrypt.CryptMsgGetParam(
cryptMsg,
WinCrypt.CMSG_ENCODED_MESSAGE,
0,
IntPtr.Zero,
ref cbData))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var vData = new byte[cbData];
// Get the encoded message.
if (!WinCrypt.CryptMsgGetParam(
cryptMsg,
WinCrypt.CMSG_ENCODED_MESSAGE,
0,
vData,
ref cbData))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var signedCms = new SignedCms();
signedCms.Decode(vData);
if (signedCms.SignerInfos.Count > 0)
{
var signerInfo = signedCms.SignerInfos[0];
if (signerInfo.Certificate != null)
{
cert = signerInfo.Certificate;
}
}
return cert;
}
It seems that if you use the "WinCrypt.CERT_QUERY_CONTENT_FLAG_ALL" on the CryptQueryObject call it suffers from the same memory performance hit as the X509Certificate object, but if you trim it down to just the PKCS7 content types it performs like a dream and seems to give me the info I need.
As I see that you've got no replies anyway, let me offer one.
If you don't mind using third-party components, take a look at TElAuthenticodeVerifier component of our SecureBlackbox product. With this component you can verify the signature and check the timestamps.
The provided answers didn't worked in my case for SHA256 signing method. I newer reached the bottom within those nested foreach loops. But Nuget AuthenticodeExaminer seams to works just fine. Here is example for single certificate with single timestamp:
var extractor = new FileInspector(#"D:\Temp\file.exe");
var signTime = extractor.GetSignatures().FirstOrDefault()?.TimestampSignatures.FirstOrDefault()?.TimestampDateTime?.UtcDateTime;
I like the idea of avoiding some nasty p/invoke code by using the SignedCms class, but be aware that under some network circumstances the SignedCms constructor can block for a long time --- I'm seeing about 15 seconds on a test I'm currently running. Alejandro Campos Magencio has some information on this on his MSDN blog in a post titled Big delay while calling EnvelopedCms constructor.