Problem Mapping Framebuffer Device Memory using C#/Mono on Raspberry PI - c#

I am trying to directly access video memory for a framebuffer video device on a Raspberry Pi using C# code running via Mono. I have a C program that works fine, but when I port it to C# it consistently fails on the "map to memory" step.
The working C program (courtesy of tasanakorn) looks like this:
#include <stdio.h>
#include <syslog.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <bcm_host.h>
int process() {
DISPMANX_DISPLAY_HANDLE_T display;
DISPMANX_MODEINFO_T display_info;
DISPMANX_RESOURCE_HANDLE_T screen_resource;
VC_IMAGE_TRANSFORM_T transform;
uint32_t image_prt;
VC_RECT_T rect1;
int ret;
int fbfd = 0;
char *fbp = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
bcm_host_init();
display = vc_dispmanx_display_open(0);
if (!display) {
syslog(LOG_ERR, "Unable to open primary display");
return -1;
}
ret = vc_dispmanx_display_get_info(display, &display_info);
if (ret) {
syslog(LOG_ERR, "Unable to get primary display information");
return -1;
}
syslog(LOG_INFO, "Primary display is %d x %d", display_info.width, display_info.height);
fbfd = open("/dev/fb1", O_RDWR);
if (fbfd == -1) {
syslog(LOG_ERR, "Unable to open secondary display");
return -1;
}
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
syslog(LOG_ERR, "Unable to get secondary display information");
return -1;
}
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
syslog(LOG_ERR, "Unable to get secondary display information");
return -1;
}
syslog(LOG_INFO, "Second display is %d x %d %dbps\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
screen_resource = vc_dispmanx_resource_create(VC_IMAGE_RGB565, vinfo.xres, vinfo.yres, &image_prt);
if (!screen_resource) {
syslog(LOG_ERR, "Unable to create screen buffer");
close(fbfd);
vc_dispmanx_display_close(display);
return -1;
}
fbp = (char*) mmap(0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
if (fbp <= 0) {
syslog(LOG_ERR, "Unable to create mamory mapping");
close(fbfd);
ret = vc_dispmanx_resource_delete(screen_resource);
vc_dispmanx_display_close(display);
return -1;
}
vc_dispmanx_rect_set(&rect1, 0, 0, vinfo.xres, vinfo.yres);
while (1) {
ret = vc_dispmanx_snapshot(display, screen_resource, 0);
vc_dispmanx_resource_read_data(screen_resource, &rect1, fbp, vinfo.xres * vinfo.bits_per_pixel / 8);
usleep(25 * 1000);
}
munmap(fbp, finfo.smem_len);
close(fbfd);
ret = vc_dispmanx_resource_delete(screen_resource);
vc_dispmanx_display_close(display);
}
int main(int argc, char **argv) {
setlogmask(LOG_UPTO(LOG_DEBUG));
openlog("fbcp", LOG_NDELAY | LOG_PID, LOG_USER);
return process();
}
The C# code (in which I've tried to port all of the C code related to /dev/fb1) is:
using System;
using System.Runtime.InteropServices;
namespace MainProgram {
class MainClass {
static void Main(string[] args) {
int fbfd = -1; // file descriptor for framebuffer device
int fbp = -1; // pointer to mapped framebuffer memory
uint fbs = 0; // size of mapped framebuffer memory
int result = 0; // utility result variable
try {
// Initialize (not sure if this is needed, but...).
Libc.bcm_host_init();
// Open the device. (Command line param is device, e.g. "/dev/fb0" or "/dev/fb1".)
fbfd = Libc.open(args[0], Libc.O_RDWR);
if (fbfd < 0)
throw new Exception("open: error " + Marshal.GetLastWin32Error());
Console.WriteLine("fbfd=" + fbfd);
// Get fixed screen info.
Libc.fb_fix_screeninfo fixInfo = new Libc.fb_fix_screeninfo();
result = Libc.ioctl1(fbfd, Libc.FBIOGET_FSCREENINFO, ref fixInfo);
if (result < 0)
throw new Exception("ioctl1: error " + Marshal.GetLastWin32Error());
Console.WriteLine("fbfix: mem start=" + fixInfo.smem_start.ToString("X8") + ", len=" + fixInfo.smem_len);
// Get variable screen info.
Libc.fb_var_screeninfo varInfo = new Libc.fb_var_screeninfo();
result = Libc.ioctl2(fbfd, Libc.FBIOGET_VSCREENINFO, ref varInfo);
if (result < 0)
throw new Exception("ioctl2: error " + Marshal.GetLastWin32Error());
Console.WriteLine("fbvar: res=" + varInfo.xres + "x" + varInfo.yres + ", bpp=" + varInfo.bits_per_pixel);
// Map framebuffer memory to virtual space.
fbs = fixInfo.smem_len;
Console.WriteLine("Confirm non-zero size: fbs=" + fbs);
fbp = Libc.mmap(0, fbs, Libc.PROT_READ | Libc.PROT_WRITE, Libc.MAP_SHARED, fbfd, 0);
if (fbp < 0)
throw new Exception("mmap: error " + Marshal.GetLastWin32Error());
Console.WriteLine("mmap: location=" + fbp.ToString("X8"));
}
catch (Exception ex) {
Console.WriteLine("*** Error: " + ex.Message);
}
finally {
if (fbp >= 0)
result = Libc.munmap(fbp, fbs);
if (fbfd >= 0)
result = Libc.close(fbfd);
};
}
public static class Libc {
public const int O_RDWR = 0x0002;
public const int PROT_READ = 0x04;
public const int PROT_WRITE = 0x02;
public const int MAP_FILE = 0x0001;
public const int MAP_SHARED = 0x0010;
public const int FBIOGET_VSCREENINFO = 0x4600;
public const int FBIOGET_FSCREENINFO = 0x4602;
[DllImport("libbcm_host.so", EntryPoint = "bcm_host_init")]
public static extern void bcm_host_init();
[DllImport("libc", EntryPoint="open", SetLastError = true)]
public static extern int open(
[MarshalAs(UnmanagedType.LPStr)] string filename,
[MarshalAs(UnmanagedType.I4)] int flags
);
[DllImport("libc", EntryPoint="close", SetLastError = true)]
public static extern int close(
[MarshalAs(UnmanagedType.I4)] int filedes
);
[DllImport("libc", EntryPoint="ioctl", SetLastError = true)]
public static extern int ioctl1(
[MarshalAs(UnmanagedType.I4)] int filedes,
[MarshalAs(UnmanagedType.I4)] int command,
ref fb_fix_screeninfo data
);
[DllImport("libc", EntryPoint="ioctl", SetLastError = true)]
public static extern int ioctl2(
[MarshalAs(UnmanagedType.I4)] int filedes,
[MarshalAs(UnmanagedType.I4)] int command,
ref fb_var_screeninfo data
);
[DllImport("libc", EntryPoint = "mmap", SetLastError = true)]
public static extern int mmap(
[MarshalAs(UnmanagedType.U4)] uint addr,
[MarshalAs(UnmanagedType.U4)] uint length,
[MarshalAs(UnmanagedType.I4)] int prot,
[MarshalAs(UnmanagedType.I4)] int flags,
[MarshalAs(UnmanagedType.I4)] int fdes,
[MarshalAs(UnmanagedType.I4)] int offset
);
[DllImport("libc", EntryPoint = "munmap", SetLastError = true)]
public static extern int munmap(
[MarshalAs(UnmanagedType.I4)] int addr,
[MarshalAs(UnmanagedType.U4)] uint length
);
[StructLayout(LayoutKind.Sequential)]
public struct fb_fix_screeninfo {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] id;
[MarshalAs(UnmanagedType.U4)] public uint smem_start;
[MarshalAs(UnmanagedType.U4)] public uint smem_len;
[MarshalAs(UnmanagedType.U4)] public uint type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)] public byte[] stuff;
};
[StructLayout(LayoutKind.Sequential)]
public struct fb_var_screeninfo {
[MarshalAs(UnmanagedType.U4)] public uint xres;
[MarshalAs(UnmanagedType.U4)] public uint yres;
[MarshalAs(UnmanagedType.U4)] public uint xres_virtual;
[MarshalAs(UnmanagedType.U4)] public uint yres_virtual;
[MarshalAs(UnmanagedType.U4)] public uint xoffset;
[MarshalAs(UnmanagedType.U4)] public uint yoffset;
[MarshalAs(UnmanagedType.U4)] public uint bits_per_pixel;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 132)] public byte[] stuff;
};
}
}
}
When I try to run the compiled code on the Pi, I get this following:
$ sudo mono ConsoleApp6.exe /dev/fb1
fbfd=4
fbfix: mem start=00000000, len=307200
fbvar: res=480x320, bpp=16
Confirm non-zero size: fbs=307200
*** Error: mmap: error 22
(I tried the same thing with the main monitor, also a framebuffer device, and got the following -- same error.)
$ sudo mono ConsoleApp6.exe /dev/fb0
fbfd=4
fbfix: mem start=1E876000, len=3686400
fbvar: res=1280x720, bpp=32
Confirm non-zero size: fbs=3686400
*** Error: mmap: error 22
Error 22 is EINVAL, which the mmap() documentation describes as, "We don't like addr, length, or offset (e.g., they are too large, or not aligned on a page boundary)." [It could also mean that: a) the length parameter is zero (which it isn't), or b) that both MAP_SHARED and MAP_PRIVATE are set (which they aren't)]. However, the values I'm passing in should be the same values used by the C code.
Does anyone have any idea what I'm doing wrong?

Solved. The problem is the constants PROT_READ, PROT_WRITE, MAP_SHARED, ... on the Raspberry Pi are not the same as the values I uncovered via Google searches (headslap). I found the correct values by looking at the .h files in /usr/include. Also, I discovered that a negative value for mmap is not necessarily an error; I needed to be looking specifically for MAP_FAILED (-1).
Here is the working code if anyone can use it:
using System;
using System.Runtime.InteropServices;
namespace MainProgram {
class MainClass {
static void Main(string[] args) {
int fbfd = -1; // file descriptor for framebuffer device
int fbp = -1; // pointer to mapped framebuffer memory
uint fbs = 0; // size of mapped framebuffer memory
int result = 0; // utility result variable
try {
// Initialize (not sure if this is needed, but...).
Libc.bcm_host_init();
// Open the device. (Command line param is device, e.g. "/dev/fb0" or "/dev/fb1".)
fbfd = Libc.open(args[0], Libc.O_RDWR);
if (fbfd == -1)
throw new Exception("open: result=" + fbfd + ", error=" + Marshal.GetLastWin32Error());
Console.WriteLine("fbfd=" + fbfd);
// Get fixed screen info.
Libc.fb_fix_screeninfo fixInfo = new Libc.fb_fix_screeninfo();
result = Libc.ioctl1(fbfd, Libc.FBIOGET_FSCREENINFO, ref fixInfo);
if (result == -1)
throw new Exception("ioctl1: result=" + result + ", error " + Marshal.GetLastWin32Error());
Console.WriteLine("fbfix: mem start=" + fixInfo.smem_start.ToString("X8") + ", len=" + fixInfo.smem_len);
// Get variable screen info.
Libc.fb_var_screeninfo varInfo = new Libc.fb_var_screeninfo();
result = Libc.ioctl2(fbfd, Libc.FBIOGET_VSCREENINFO, ref varInfo);
if (result == -1)
throw new Exception("ioctl2: result=" + result + ", error " + Marshal.GetLastWin32Error());
Console.WriteLine("fbvar: res=" + varInfo.xres + "x" + varInfo.yres + ", bpp=" + varInfo.bits_per_pixel);
// Map framebuffer memory to virtual space.
fbp = Libc.mmap(0, fixInfo.smem_len, Libc.PROT_READ | Libc.PROT_WRITE, Libc.MAP_SHARED, fbfd, 0);
if (fbp == Libc.MAP_FAILED)
throw new Exception("mmap: result=" + fbp + ", error " + Marshal.GetLastWin32Error());
Console.WriteLine("mmap: location=" + fbp.ToString("X8"));
}
catch (Exception ex) {
Console.WriteLine("*** Error: " + ex.Message);
}
finally {
if (fbp != -1)
result = Libc.munmap(fbp, fbs);
if (fbfd != -1)
result = Libc.close(fbfd);
};
}
public static class Libc {
public const int O_RDWR = 0x0002;
public const int PROT_READ = 0x1;
public const int PROT_WRITE = 0x2;
public const int MAP_SHARED = 0x01;
public const int MAP_FAILED = -1;
public const int FBIOGET_VSCREENINFO = 0x4600;
public const int FBIOGET_FSCREENINFO = 0x4602;
[DllImport("libbcm_host.so", EntryPoint = "bcm_host_init")]
public static extern void bcm_host_init();
[DllImport("libc", EntryPoint="open", SetLastError = true)]
public static extern int open(
[MarshalAs(UnmanagedType.LPStr)] string filename,
[MarshalAs(UnmanagedType.I4)] int flags
);
[DllImport("libc", EntryPoint="close", SetLastError = true)]
public static extern int close(
[MarshalAs(UnmanagedType.I4)] int filedes
);
[DllImport("libc", EntryPoint="ioctl", SetLastError = true)]
public static extern int ioctl1(
[MarshalAs(UnmanagedType.I4)] int filedes,
[MarshalAs(UnmanagedType.I4)] int command,
ref fb_fix_screeninfo data
);
[DllImport("libc", EntryPoint="ioctl", SetLastError = true)]
public static extern int ioctl2(
[MarshalAs(UnmanagedType.I4)] int filedes,
[MarshalAs(UnmanagedType.I4)] int command,
ref fb_var_screeninfo data
);
[DllImport("libc", EntryPoint = "mmap", SetLastError = true)]
public static extern int mmap(
[MarshalAs(UnmanagedType.U4)] uint addr,
[MarshalAs(UnmanagedType.U4)] uint length,
[MarshalAs(UnmanagedType.I4)] int prot,
[MarshalAs(UnmanagedType.I4)] int flags,
[MarshalAs(UnmanagedType.I4)] int fdes,
[MarshalAs(UnmanagedType.I4)] int offset
);
[DllImport("libc", EntryPoint = "munmap", SetLastError = true)]
public static extern int munmap(
[MarshalAs(UnmanagedType.I4)] int addr,
[MarshalAs(UnmanagedType.U4)] uint length
);
[StructLayout(LayoutKind.Sequential)]
public struct fb_fix_screeninfo {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] id;
[MarshalAs(UnmanagedType.U4)] public uint smem_start;
[MarshalAs(UnmanagedType.U4)] public uint smem_len;
[MarshalAs(UnmanagedType.U4)] public uint type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)] public byte[] stuff;
};
[StructLayout(LayoutKind.Sequential)]
public struct fb_var_screeninfo {
[MarshalAs(UnmanagedType.U4)] public uint xres;
[MarshalAs(UnmanagedType.U4)] public uint yres;
[MarshalAs(UnmanagedType.U4)] public uint xres_virtual;
[MarshalAs(UnmanagedType.U4)] public uint yres_virtual;
[MarshalAs(UnmanagedType.U4)] public uint xoffset;
[MarshalAs(UnmanagedType.U4)] public uint yoffset;
[MarshalAs(UnmanagedType.U4)] public uint bits_per_pixel;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 132)] public byte[] stuff;
};
}
}
}

Related

How to get list of directories and files with full path using win32 in c# application

Below console application in c#.net shows only folder names in D drive without full path name but I want to have iterate recursively inside the folders , subfolder and files within those subfolders with help of win32 API because of I have to scan folders having thousands of files.
Win32 API works fast so I m not using regular c#.net function for getting directories and its files.
class Program
{
// Kernal32.dll import for File management operations.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr FindFirstFileEx(
string lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
out WIN32_FIND_DATA lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
IntPtr lpSearchFilter,
int dwAdditionalFlags);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll")]
static extern bool FindClose(IntPtr hFindFile);
public const int FIND_FIRST_EX_CASE_SENSITIVE = 1;
public const int FIND_FIRST_EX_LARGE_FETCH = 2;
static void Main()
{
WIN32_FIND_DATA findData;
FINDEX_INFO_LEVELS findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
int additionalFlags = 0;
if (Environment.OSVersion.Version.Major >= 6)
{
findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
//additionalFlags = FIND_FIRST_EX_LARGE_FETCH;
}
string pattern = "D:\\*.*";
IntPtr hFile = FindFirstFileEx(
pattern,
findInfoLevel,
out findData,
FINDEX_SEARCH_OPS.FindExSearchNameMatch,
IntPtr.Zero,
additionalFlags);
int error = Marshal.GetLastWin32Error();
if (hFile.ToInt32() != -1)
{
do
{
Console.WriteLine("Found file {0}, & Attribute is {1}", findData.cFileName, findData.dwFileAttributes);
}
while (FindNextFile(hFile, out findData));
FindClose(hFile);
}
Console.ReadLine();
}
}
Below is required enum and struct for program.
public enum FINDEX_SEARCH_OPS
{
FindExSearchNameMatch = 0,
FindExSearchLimitToDirectories = 1,
FindExSearchLimitToDevices = 2
}
public enum FINDEX_INFO_LEVELS
{
FindExInfoStandard = 0,
FindExInfoBasic = 1
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
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;
}
I have got code from Pinvoke.net and website is http://pinvoke.net/
You can put your code into a rekursive function, and call it for each folder:
static void Main()
{
WIN32_FIND_DATA findData;
FINDEX_INFO_LEVELS findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
int additionalFlags = 0;
if (Environment.OSVersion.Version.Major >= 6)
{
findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
//additionalFlags = FIND_FIRST_EX_LARGE_FETCH;
}
string startFolder = #"D:\";
var watch = new Stopwatch();
watch.Start();
IterateFolder(findInfoLevel, additionalFlags, startFolder);
watch.Stop();
Console.WriteLine();
Console.WriteLine($"{watch.Elapsed.TotalSeconds}");
Console.ReadLine();
}
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x10;
private static void IterateFolder(FINDEX_INFO_LEVELS findInfoLevel, int additionalFlags, string folder)
{
WIN32_FIND_DATA findData;
IntPtr hFile = FindFirstFileEx(
$"{folder}\\*.*",
findInfoLevel,
out findData,
FINDEX_SEARCH_OPS.FindExSearchNameMatch,
IntPtr.Zero,
additionalFlags);
int error = Marshal.GetLastWin32Error();
if (hFile.ToInt32() != -1)
{
do
{
if (findData.cFileName == "." || findData.cFileName == "..") continue;//ignore folder navigation
var foundFolder = $"{folder}\\{findData.cFileName}";
Console.WriteLine("Found file {0}, & Attribute is {1}", foundFolder, findData.dwFileAttributes);
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0)
{
//Recursive for directories
IterateFolder(findInfoLevel, additionalFlags, foundFolder);
}
}
while (FindNextFile(hFile, out findData));
FindClose(hFile);
}
}

PInvoke NetLocalGroupGetMembers runs into FatalExecutionEngineError

I need to use win32 NetLocalGroupGetMembers in C#. I found and tested three solutions. All three fail with an FatalExecutionEngineError. The framework is .net 4.0
Here is a full example:
Reference to the api:
static class NetworkAPI
{
[DllImport("Netapi32.dll")]
public extern static int NetLocalGroupGetMembers([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string localgroupname, int level, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out int resumehandle);
[DllImport("Netapi32.dll")]
public extern static int NetApiBufferFree(IntPtr Buffer);
// LOCALGROUP_MEMBERS_INFO_1 - Structure for holding members details
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct LOCALGROUP_MEMBERS_INFO_1
{
public int lgrmi1_sid;
public int lgrmi1_sidusage;
public string lgrmi1_name;
}
}
calling the function:
static void Main(string[] args)
{
int EntriesRead;
int TotalEntries;
int Resume;
IntPtr bufPtr;
string groupName = "Administrators";
NetworkAPI.NetLocalGroupGetMembers(null, groupName, 1, out bufPtr, -1, out EntriesRead, out TotalEntries, out Resume);
if (EntriesRead > 0)
{
NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[] Members = new NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[EntriesRead];
IntPtr iter = bufPtr;
// EntriesRead has the correct quantity of members of the group, so the group is found
for (int i = 0; i < EntriesRead; i++)
{
// --------------------------------------------------
// ==> here the FatalExecutionEngineError happens:
Members[i] = (NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)Marshal.PtrToStructure(iter, typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));
//
// --------------------------------------------------
iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)));
Console.WriteLine(Members[i].lgrmi1_name);
}
NetworkAPI.NetApiBufferFree(bufPtr);
}
}
I see the following errors:
The resume handle is a pointer. Use ref IntPtr resumehandle for that parameter, and pass IntPtr.Zero on the first call. Or if you don't need to use a resume handle declare the parameter as IntPtr resumehandle and pass IntPtr.Zero. Consult the function documentation on MSDN for the full details.
The lgrmi1_sid member of the struct is a pointer. Declare it as such: public IntPtr lgrmi1_sid.
Casting an IntPtr to an int will lead to pointer truncation on 64 bit. Either use arithmetic directly on the IntPtr, or for older C# versions cast to long. The former is better, like so: iter += Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));.
You do not check the return value for errors.
Fix those errors and your program will run correctly.
For the sake of completeness, here is the code how to pinvoke
NetLocalGroupGetMembers.
I corrected the code as David suggested. There is also a suggestion from Martin Liversage which I didn't implement. But it maybe usefull.
If you like it, please do not upvode this answer but upvote Davids answer, who found the errors.
Reference to the api:
public static class NetworkAPI
{
[DllImport("Netapi32.dll")]
public extern static uint NetLocalGroupGetMembers([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string localgroupname, int level, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out IntPtr resumehandle);
[DllImport("Netapi32.dll")]
public extern static int NetApiBufferFree(IntPtr Buffer);
// LOCALGROUP_MEMBERS_INFO_1 - Structure for holding members details
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct LOCALGROUP_MEMBERS_INFO_1
{
public IntPtr lgrmi1_sid;
public int lgrmi1_sidusage;
public string lgrmi1_name;
}
// documented in MSDN
public const uint ERROR_ACCESS_DENIED = 0x0000005;
public const uint ERROR_MORE_DATA = 0x00000EA;
public const uint ERROR_NO_SUCH_ALIAS = 0x0000560;
public const uint NERR_InvalidComputer = 0x000092F;
// found by testing
public const uint NERR_GroupNotFound = 0x00008AC;
public const uint SERVER_UNAVAILABLE = 0x0006BA;
}
Calling the function:
static void Main(string[] args)
{
int EntriesRead;
int TotalEntries;
IntPtr Resume;
IntPtr bufPtr;
string groupName = "Administratoren";
string computerName = null; // null for the local machine
uint retVal = NetworkAPI.NetLocalGroupGetMembers(computerName, groupName, 1, out bufPtr, -1, out EntriesRead, out TotalEntries, out Resume);
if(retVal != 0)
{
if (retVal == NetworkAPI.ERROR_ACCESS_DENIED) { Console.WriteLine("Access denied"); return; }
if (retVal == NetworkAPI.ERROR_MORE_DATA) { Console.WriteLine("ERROR_MORE_DATA"); return; }
if (retVal == NetworkAPI.ERROR_NO_SUCH_ALIAS) { Console.WriteLine("Group not found"); return; }
if (retVal == NetworkAPI.NERR_InvalidComputer) { Console.WriteLine("Invalid computer name"); return; }
if (retVal == NetworkAPI.NERR_GroupNotFound) { Console.WriteLine("Group not found"); return; }
if (retVal == NetworkAPI.SERVER_UNAVAILABLE) { Console.WriteLine("Server unavailable"); return; }
Console.WriteLine("Unexpected NET_API_STATUS: " + retVal.ToString());
return;
}
if (EntriesRead > 0)
{
NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[] Members = new NetworkAPI.LOCALGROUP_MEMBERS_INFO_1[EntriesRead];
IntPtr iter = bufPtr;
for (int i = 0; i < EntriesRead; i++)
{
Members[i] = (NetworkAPI.LOCALGROUP_MEMBERS_INFO_1)Marshal.PtrToStructure(iter, typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));
//x64 safe
iter += Marshal.SizeOf(typeof(NetworkAPI.LOCALGROUP_MEMBERS_INFO_1));
Console.WriteLine(Members[i].lgrmi1_name);
}
NetworkAPI.NetApiBufferFree(bufPtr);
}
}

Get mifare card serial number with WinSCard library

I am working on an application that required reading the mifare card serial number, the language I am working with is C#.
I am new to mifare reader programming, so I am sorry for asking dumb question.
At first I would like to know if there is a different between Mifare UID and Mifare Serial number.
I have managed to get UID with the help of WinSCard library, but I cant figure it out how to get the card serial number which should be 10 digit numbers.
I do appreciate if you could point me to the right direction.
Thanks in advance for the help.
Regards
C# signature of SCardTransmit method
[StructLayout(LayoutKind.Sequential)]
public struct SCARD_IO_REQUEST
{
public int dwProtocol;
public int cbPciLength;
}
[DllImport("winscard.dll")]
public static extern int SCardTransmit(int hCard, ref SCARD_IO_REQUEST pioSendRequest, ref byte SendBuff, int SendBuffLen, ref SCARD_IO_REQUEST pioRecvRequest,
ref byte RecvBuff, ref int RecvBuffLen);
Code Example for read UID of mifare card
private SmartcardErrorCode GetUID(ref byte[] UID)
{
byte[] receivedUID = new byte[10];
UnsafeNativeMethods.SCARD_IO_REQUEST request = new UnsafeNativeMethods.SCARD_IO_REQUEST();
request.dwProtocol = 1; //SCARD_PROTOCOL_T1);
request.cbPciLength = System.Runtime.InteropServices.Marshal.SizeOf(typeof(UnsafeNativeMethods.SCARD_IO_REQUEST));
byte[] sendBytes = new byte[] { 0xFF, 0xCA, 0x00, 0x00, 0x04 }; //get UID command for Mifare cards
int outBytes = receivedUID.Length;
int status = SCardTransmit(_hCard, ref request, ref sendBytes[0], sendBytes.Length, ref request, ref receivedUID[0], ref outBytes);
UID = receivedUID.Take(8).ToArray();
return status;
}
The Selected answer doesn't work on x64 enviroments.
Here is a Full example code, tested and working on Windows 10 x64.
What this does:
Retrieve the List of Available Readers
Selects to use the First available Reader
Check if there is a Card in the reader and Connects to it.
Transmits to the card the Command to retrieve its UID
Converts the UID into a Hexadecimal string.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace DXCutcsa.VO.Common
{
public class SmartCardReader
{
#region Atributos
private bool _disposed = false;
private IntPtr _ReaderContext = IntPtr.Zero;
private string _ReaderName = string.Empty;
private List<string> _AvailableReaders = new List<string>();
#endregion
#region Win32 APIs
[DllImport("WinScard.dll")]
static extern int SCardEstablishContext(uint dwScope,
IntPtr notUsed1,
IntPtr notUsed2,
out IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardReleaseContext(IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardConnect(IntPtr hContext,
string cReaderName,
uint dwShareMode,
uint dwPrefProtocol,
ref IntPtr phCard,
ref IntPtr ActiveProtocol);
[DllImport("WinScard.dll")]
static extern int SCardDisconnect(IntPtr hCard, int Disposition);
[DllImport("WinScard.dll", EntryPoint = "SCardListReadersA", CharSet = CharSet.Ansi)]
static extern int SCardListReaders(
IntPtr hContext,
byte[] mszGroups,
byte[] mszReaders,
ref UInt32 pcchReaders);
[DllImport("winscard.dll")]
static extern int SCardStatus(
uint hCard,
IntPtr szReaderName,
ref int pcchReaderLen,
ref int pdwState,
ref uint pdwProtocol,
byte[] pbAtr,
ref int pcbAtrLen);
[DllImport("winscard.dll")]
static extern int SCardTransmit(
IntPtr hCard,
ref SCARD_IO_REQUEST pioSendRequest,
ref byte SendBuff,
uint SendBuffLen,
ref SCARD_IO_REQUEST pioRecvRequest,
byte[] RecvBuff,
ref uint RecvBuffLen);
[DllImport("winscard.dll", SetLastError = true)]
static extern int SCardGetAttrib(
IntPtr hCard, // Valor retornado en funcion ScardConnect
UInt32 dwAttrId, // Identificacion de atributos a obtener
byte[] pbAttr, // Puntero al vector atributos recividos
ref IntPtr pcbAttrLen // TamaƱo del vector
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct ReaderState
{
public ReaderState(string sName)
{
this.szReader = sName;
this.pvUserData = IntPtr.Zero;
this.dwCurrentState = 0;
this.dwEventState = 0;
this.cbATR = 0;
this.rgbATR = null;
}
internal string szReader;
internal IntPtr pvUserData;
internal uint dwCurrentState;
internal uint dwEventState;
internal uint cbATR; // count of bytes in rgbATR
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x24, ArraySubType = UnmanagedType.U1)]
internal byte[] rgbATR;
}
[StructLayout(LayoutKind.Sequential)]
public struct SCARD_IO_REQUEST
{
public UInt32 dwProtocol;
public UInt32 cbPciLength;
}
/*****************************************************************/
[DllImport("kernel32.dll", SetLastError = true)]
private extern static IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll")]
private extern static void FreeLibrary(IntPtr handle);
[DllImport("kernel32.dll")]
private extern static IntPtr GetProcAddress(IntPtr handle, string procName);
#endregion
#region Error codes
public const uint S_SUCCESS = 0x00000000;
public const uint F_INTERNAL_ERROR = 0x80100001;
public const uint E_CANCELLED = 0x80100002;
public const uint E_INVALID_HANDLE = 0x80100003;
public const uint E_INVALID_PARAMETER = 0x80100004;
public const uint E_INVALID_TARGET = 0x80100005;
public const uint E_NO_MEMORY = 0x80100006;
public const uint F_WAITED_TOO_LONG = 0x80100007;
public const uint E_INSUFFICIENT_BUFFER = 0x80100008;
public const uint E_UNKNOWN_READER = 0x80100009;
public const uint E_TIMEOUT = 0x8010000A;
public const uint E_SHARING_VIOLATION = 0x8010000B;
public const uint E_NO_SMARTCARD = 0x8010000C;
public const uint E_UNKNOWN_CARD = 0x8010000D;
public const uint E_CANT_DISPOSE = 0x8010000E;
public const uint E_PROTO_MISMATCH = 0x8010000F;
public const uint E_NOT_READY = 0x80100010;
public const uint E_INVALID_VALUE = 0x80100011;
public const uint E_SYSTEM_CANCELLED = 0x80100012;
public const uint F_COMM_ERROR = 0x80100013;
public const uint F_UNKNOWN_ERROR = 0x80100014;
public const uint E_INVALID_ATR = 0x80100015;
public const uint E_NOT_TRANSACTED = 0x80100016;
public const uint E_READER_UNAVAILABLE = 0x80100017;
public const uint P_SHUTDOWN = 0x80100018;
public const uint E_PCI_TOO_SMALL = 0x80100019;
public const uint E_READER_UNSUPPORTED = 0x8010001A;
public const uint E_DUPLICATE_READER = 0x8010001B;
public const uint E_CARD_UNSUPPORTED = 0x8010001C;
public const uint E_NO_SERVICE = 0x8010001D;
public const uint E_SERVICE_STOPPED = 0x8010001E;
public const uint E_UNEXPECTED = 0x8010001F;
public const uint E_ICC_INSTALLATION = 0x80100020;
public const uint E_ICC_CREATEORDER = 0x80100021;
public const uint E_UNSUPPORTED_FEATURE = 0x80100022;
public const uint E_DIR_NOT_FOUND = 0x80100023;
public const uint E_FILE_NOT_FOUND = 0x80100024;
public const uint E_NO_DIR = 0x80100025;
public const uint E_NO_FILE = 0x80100026;
public const uint E_NO_ACCESS = 0x80100027;
public const uint E_WRITE_TOO_MANY = 0x80100028;
public const uint E_BAD_SEEK = 0x80100029;
public const uint E_INVALID_CHV = 0x8010002A;
public const uint E_UNKNOWN_RES_MNG = 0x8010002B;
public const uint E_NO_SUCH_CERTIFICATE = 0x8010002C;
public const uint E_CERTIFICATE_UNAVAILABLE = 0x8010002D;
public const uint E_NO_READERS_AVAILABLE = 0x8010002E;
public const uint E_COMM_DATA_LOST = 0x8010002F;
public const uint E_NO_KEY_CONTAINER = 0x80100030;
public const uint W_UNSUPPORTED_CARD = 0x80100065;
public const uint W_UNRESPONSIVE_CARD = 0x80100066;
public const uint W_UNPOWERED_CARD = 0x80100067;
public const uint W_RESET_CARD = 0x80100068;
public const uint W_REMOVED_CARD = 0x80100069;
public const uint W_SECURITY_VIOLATION = 0x8010006A;
public const uint W_WRONG_CHV = 0x8010006B;
public const uint W_CHV_BLOCKED = 0x8010006C;
public const uint W_EOF = 0x8010006D;
public const uint W_CANCELLED_BY_USER = 0x8010006E;
public const uint W_CARD_NOT_AUTHENTICATED = 0x8010006F;
#endregion
#region Constructor
public SmartCardReader()
{
}
#endregion
#region Metodos
public long GetUID(ref byte[] UID)
{
long _result = 0;
bool cardInserted = false;
IntPtr _CardContext = IntPtr.Zero;
IntPtr ActiveProtocol = IntPtr.Zero;
// Establish Reader context:
if (this._ReaderContext == IntPtr.Zero)
{
_result = SCardEstablishContext(2, IntPtr.Zero, IntPtr.Zero, out this._ReaderContext);
#region Get List of Available Readers
uint pcchReaders = 0;
int nullindex = -1;
char nullchar = (char)0;
// First call with 3rd parameter set to null gets readers buffer length.
_result = SCardListReaders(this._ReaderContext, null, null, ref pcchReaders);
byte[] mszReaders = new byte[pcchReaders];
// Fill readers buffer with second call.
_result = SCardListReaders(this._ReaderContext, null, mszReaders, ref pcchReaders);
// Populate List with readers.
string currbuff = new ASCIIEncoding().GetString(mszReaders);
int len = (int)pcchReaders;
if (len > 0)
{
while (currbuff[0] != nullchar)
{
nullindex = currbuff.IndexOf(nullchar); // Get null end character.
string reader = currbuff.Substring(0, nullindex);
this._AvailableReaders.Add(reader);
len = len - (reader.Length + 1);
currbuff = currbuff.Substring(nullindex + 1, len);
}
}
#endregion
// Select the first reader:
this._ReaderName = this._AvailableReaders[0];
}
try
{
//Check if there is a Card in the reader:
//dwShareMode: SCARD_SHARE_SHARED = 0x00000002 - This application will allow others to share the reader
//dwPreferredProtocols: SCARD_PROTOCOL_T0 - Use the T=0 protocol (value = 0x00000001)
if (this._ReaderContext != IntPtr.Zero)
{
_result = SCardConnect(this._ReaderContext, this._ReaderName, 0x00000002, 0x00000001, ref _CardContext, ref ActiveProtocol);
if (_result == 0)
{
cardInserted = true;
SCARD_IO_REQUEST request = new SCARD_IO_REQUEST()
{
dwProtocol = (uint)ActiveProtocol,
cbPciLength = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(SCARD_IO_REQUEST))
};
byte[] sendBytes = new byte[] { 0xFF, 0xCA, 0x00, 0x00, 0x00 }; //<- get UID command for iClass cards
byte[] ret_Bytes = new byte[33];
uint sendLen = (uint)sendBytes.Length;
uint ret_Len = (uint)ret_Bytes.Length;
_result = SCardTransmit(_CardContext, ref request, ref sendBytes[0], sendLen, ref request, ret_Bytes, ref ret_Len);
if (_result == 0)
{
UID = ret_Bytes.Take(4).ToArray(); //only take the first 8, the last 2 bytes are not part of the UID of the card
string dataOut = byteToHexa(UID, UID.Length, true).Trim(); //Devolver la respuesta en Hexadecimal
}
}
}
}
finally
{
SCardDisconnect(_CardContext, 0);
SCardReleaseContext(this._ReaderContext);
}
return _result;
}
private string byteToHexa(byte[] byReadBuffer, int leng, bool bSpace)
{
string text2 = "";
for (short num1 = 0; num1 < leng; num1 = (short)(num1 + 1))
{
short num2 = byReadBuffer[num1];
text2 = text2 + System.Convert.ToString(num2, 16).PadLeft(2, '0');
if (bSpace)
{
text2 = text2 + " ";
}
}
return text2.ToUpper();
}
//Get the address of Pci from "Winscard.dll".
private IntPtr GetPciT0()
{
IntPtr handle = LoadLibrary("Winscard.dll");
IntPtr pci = GetProcAddress(handle, "g_rgSCardT0Pci");
FreeLibrary(handle);
return pci;
}
#endregion
}// Fin de la Clase
}
PInvoke docs helped a lot: https://www.pinvoke.net/default.aspx/winscard.scardtransmit

Read environment variables from a process in C#

I want to read the environment variables of process B from C# code in process A. I have seen some solutions for this in C++ but haven't tried adapting these to C#.
Is this possible from C#, and if not, has anyone wrapped a C++ solution yet?
I've skimmed through the links provided by Isalamon and Daniel Hilgarth, and also the code in CLR Profiler's GetServicesEnvironment() method which seems to be doing the same thing, and after a bit of testing found that the most reliable solution is Oleksiy's code (pure C# with P/Invoke) which he published in this blog post. It still has the limitation, where you have to be a 64bit process to read the env vars of another 64bit process.
Windows solution (32 bit CLR, can access 32 and 64 bit processes),
slightly modified from https://stackoverflow.com/a/46006415 :
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
// Slightly modified from
// https://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c
// https://stackoverflow.com/a/46006415
public static class ProcessCommandLine
{
private static class Win32Native
{
public const uint PROCESS_BASIC_INFORMATION = 0;
[Flags]
public enum OpenProcessDesiredAccessFlags : uint
{
PROCESS_VM_READ = 0x0010,
PROCESS_QUERY_INFORMATION = 0x0400,
}
[StructLayout(LayoutKind.Sequential)]
public struct ProcessBasicInformation
{
public IntPtr Reserved1;
public IntPtr PebBaseAddress;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] Reserved2;
public IntPtr UniqueProcessId;
public IntPtr ParentProcessId;
}
[StructLayout(LayoutKind.Sequential)]
public struct ProcessBasicInformation64
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] Reserved1;
public UInt64 PebBaseAddress;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] Reserved2;
public IntPtr UniqueProcessId;
public IntPtr Reserved3;
public IntPtr ParentProcessId;
public IntPtr Reserved4;
}
[StructLayout(LayoutKind.Sequential)]
public struct UnicodeString
{
public ushort Length;
public ushort MaximumLength;
public IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential)]
public struct UnicodeString64
{
public ushort Length;
public ushort MaximumLength;
public UInt32 __padding;
public UInt64 Buffer;
}
// This is not the real struct!
// I faked it to get ProcessParameters address.
// Actual struct definition:
// https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb
[StructLayout(LayoutKind.Sequential)]
public struct PEB
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] Reserved;
public IntPtr ProcessParameters;
}
[StructLayout(LayoutKind.Sequential)]
public struct PEB64
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public IntPtr[] Reserved;
public UInt64 ProcessParameters;
}
/*
* https://stackoverflow.com/a/63222041
Left: Right:
32-bit struct 64-bit struct
0
ULONG MaximumLength; 0
ULONG Length; 4
ULONG Flags; 8
ULONG DebugFlags; 0c
10
PVOID ConsoleHandle; 10
ULONG ConsoleFlags; 18 +4
PVOID StdInputHandle; 20
PVOID StdOutputHandle; 28
20
PVOID StdErrorHandle; 30
UNICODE_STRING CurrentDirectoryPath; 38
PVOID CurrentDirectoryHandle; 48
30
UNICODE_STRING DllPath; 50
UNICODE_STRING ImagePathName; 60
40
UNICODE_STRING CommandLine 70
PVOID Environment; 80
ULONG StartingPositionLeft;
50
ULONG StartingPositionTop;\
ULONG Width;\
ULONG Height;\
ULONG CharWidth;\
60
ULONG CharHeight;\
ULONG ConsoleTextAttributes;\
ULONG WindowFlags;\
ULONG ShowWindowFlags;\
70
UNICODE_STRING WindowTitle; b0
UNICODE_STRING DesktopName; c0
80
UNICODE_STRING ShellInfo; d0
UNICODE_STRING RuntimeData; e0
90
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[32];\
ULONG EnvironmentSize;\
*/
[StructLayout(LayoutKind.Sequential)]
public struct RtlUserProcessParameters
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Reserved1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public IntPtr[] Reserved2;
public UnicodeString ImagePathName;
public UnicodeString CommandLine;
public IntPtr Environment;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public IntPtr[] Reserved3; // StartingPositionLeft, -Top, Width, Height, CharWidth, -Height, ConsoleTextAttributes, WindowFlags, ShowWindowFlags
public UnicodeString WindowTitle;
public UnicodeString DesktopName;
public UnicodeString ShellInfo;
public UnicodeString RuntimeData;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32 * 4)]
public IntPtr[] Reserved4;
public uint EnvironmentSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct RtlUserProcessParameters64
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Reserved1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public IntPtr[] Reserved2;
public UnicodeString64 CurrentDirectoryPath;
public UnicodeString64 DllPath;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] Reserved2b;
public UnicodeString64 ImagePathName;
public UnicodeString64 CommandLine;
public UInt64 Environment;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public IntPtr[] Reserved3; // StartingPositionLeft, -Top, Width, Height, CharWidth, -Height, ConsoleTextAttributes, WindowFlags, ShowWindowFlags
public UnicodeString64 WindowTitle;
public UnicodeString64 DesktopName;
public UnicodeString64 ShellInfo;
public UnicodeString64 RuntimeData;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32 * 6)]
public IntPtr[] Reserved4;
public uint EnvironmentSize;
}
[DllImport("ntdll.dll")]
public static extern uint NtQueryInformationProcess(
IntPtr ProcessHandle,
uint ProcessInformationClass,
IntPtr ProcessInformation,
uint ProcessInformationLength,
out uint ReturnLength);
[DllImport("ntdll.dll")]
public static extern uint NtWow64QueryInformationProcess64(
IntPtr ProcessHandle,
uint ProcessInformationClass,
IntPtr ProcessInformation,
uint ProcessInformationLength,
out uint ReturnLength);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(
OpenProcessDesiredAccessFlags dwDesiredAccess,
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
uint dwProcessId);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ReadProcessMemory(
IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer,
uint nSize, out uint lpNumberOfBytesRead);
[DllImport("ntdll.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool NtWow64ReadVirtualMemory64(
IntPtr hProcess, UInt64 lpBaseAddress, IntPtr lpBuffer,
UInt64 nSize, out UInt64 lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("shell32.dll", SetLastError = true,
CharSet = CharSet.Unicode, EntryPoint = "CommandLineToArgvW")]
public static extern IntPtr CommandLineToArgv(string lpCmdLine, out int pNumArgs);
}
private static bool ReadProcessMemory(IntPtr hProcess, UInt64 lpBaseAddress, IntPtr mem, UInt32 sz, bool force64)
{
if (!force64 && (ulong)0x100000000 > lpBaseAddress)
return Win32Native.ReadProcessMemory(hProcess, (IntPtr)lpBaseAddress, mem, sz, out UInt32 read) && read == sz;
Win32Native.NtWow64ReadVirtualMemory64(hProcess, lpBaseAddress, mem, sz, out UInt64 len64);
return len64 == sz;
}
private static bool ReadStructFromProcessMemory<TStruct>(
IntPtr hProcess, IntPtr lpBaseAddress, out TStruct val, bool bit64 = false)
{
return ReadStructFromProcessMemory<TStruct>(hProcess, (UInt64)lpBaseAddress, out val, bit64);
}
private static bool ReadStructFromProcessMemory<TStruct>(
IntPtr hProcess, UInt64 lpBaseAddress, out TStruct val, bool bit64 = false)
{
val = default;
var structSize = Marshal.SizeOf<TStruct>();
var mem = Marshal.AllocHGlobal(structSize);
try
{
bool ret = ReadProcessMemory(hProcess, lpBaseAddress, mem, (UInt32)structSize, bit64);
if (ret)
{
val = Marshal.PtrToStructure<TStruct>(mem);
return true;
}
}
finally
{
Marshal.FreeHGlobal(mem);
}
return false;
}
public static string ErrorToString(int error) =>
new string[]
{
"Success",
"Failed to open process for reading",
"Failed to query process information",
"PEB address was null",
"Failed to read PEB information",
"Failed to read process parameters",
"Failed to read command line from process",
"Failed to read environment from process",
}[Math.Abs(error)];
public static int Retrieve(Process process, out string commandLine, out List<string> environment)
{
int rc = 0;
commandLine = null;
environment = null;
var hProcess = Win32Native.OpenProcess(
Win32Native.OpenProcessDesiredAccessFlags.PROCESS_QUERY_INFORMATION |
Win32Native.OpenProcessDesiredAccessFlags.PROCESS_VM_READ, false, (uint)process.Id);
if (hProcess != IntPtr.Zero)
{
try
{
var sizePBI = Marshal.SizeOf<Win32Native.ProcessBasicInformation>();
var sizePBI64 = Marshal.SizeOf<Win32Native.ProcessBasicInformation64>();
var memPBI = Marshal.AllocHGlobal(sizePBI64);
try
{
bool bit64;
var ret = Win32Native.NtQueryInformationProcess(
hProcess, Win32Native.PROCESS_BASIC_INFORMATION, memPBI,
(uint)sizePBI, out var len);
if (0 == ret)
{
var pbiInfo64 = new Win32Native.ProcessBasicInformation64();
var pbiInfo = Marshal.PtrToStructure<Win32Native.ProcessBasicInformation>(memPBI);
if (pbiInfo.PebBaseAddress != IntPtr.Zero)
{
bit64 = false;
}
else
{
bit64 = true;
ret = Win32Native.NtWow64QueryInformationProcess64(
hProcess, Win32Native.PROCESS_BASIC_INFORMATION, memPBI,
(uint)sizePBI64, out len);
pbiInfo64 = Marshal.PtrToStructure<Win32Native.ProcessBasicInformation64>(memPBI);
}
if (pbiInfo64.PebBaseAddress != (UInt64)0)
{
Win32Native.PEB64 pebInfo64;
Win32Native.PEB pebInfo;
pebInfo.ProcessParameters = (IntPtr)0; pebInfo64.ProcessParameters = (UInt64)0;
bool ok;
if (bit64)
ok = ReadStructFromProcessMemory<Win32Native.PEB64>(hProcess, pbiInfo64.PebBaseAddress, out pebInfo64, bit64);
else
ok = ReadStructFromProcessMemory<Win32Native.PEB>(hProcess, pbiInfo.PebBaseAddress, out pebInfo, bit64);
if (ok)
{
UInt32 CommandLineSize = 0;
UInt64 CommandLineBuf = 0;
UInt32 EnvironmentSize = 0;
UInt64 EnvironmentBuf = 0;
if (bit64)
{
ok = ReadStructFromProcessMemory<Win32Native.RtlUserProcessParameters64>(hProcess, pebInfo64.ProcessParameters, out var ruppInfo64, bit64);
CommandLineSize = (UInt32)ruppInfo64.CommandLine.MaximumLength;
CommandLineBuf = ruppInfo64.CommandLine.Buffer;
EnvironmentSize = (UInt32)ruppInfo64.EnvironmentSize;
EnvironmentBuf = ruppInfo64.Environment;
}
else
{
ok = ReadStructFromProcessMemory<Win32Native.RtlUserProcessParameters>(hProcess, (UInt64)pebInfo.ProcessParameters, out var ruppInfo, bit64);
CommandLineSize = ruppInfo.CommandLine.MaximumLength;
CommandLineBuf = (UInt64)ruppInfo.CommandLine.Buffer;
EnvironmentSize = ruppInfo.EnvironmentSize;
EnvironmentBuf = (UInt64)ruppInfo.Environment;
}
if (ok)
{
var memCL = Marshal.AllocHGlobal((IntPtr)CommandLineSize);
try
{
if (ReadProcessMemory(hProcess, CommandLineBuf, memCL, CommandLineSize, bit64))
{
commandLine = Marshal.PtrToStringUni(memCL);
rc = 0;
}
else
{
// couldn't read command line buffer
rc = -6;
}
}
finally
{
Marshal.FreeHGlobal(memCL);
}
memCL = Marshal.AllocHGlobal((IntPtr)EnvironmentSize);
try
{
if (ReadProcessMemory(hProcess, EnvironmentBuf, memCL, EnvironmentSize, bit64))
{
environment = new List<string>(40);
IntPtr readPtr = memCL;
while (EnvironmentSize > 0 && Marshal.ReadInt16(readPtr) != 0)
{
string slice = Marshal.PtrToStringUni(readPtr);
environment.Add(slice);
uint size = 0;
do
{
size += 2;
readPtr += 2;
}
while (Marshal.ReadInt16(readPtr) != 0);
size += 2;
readPtr += 2;
EnvironmentSize -= size;
}
rc = 0;
}
else
{
// couldn't read command line buffer
rc = -7;
}
}
finally
{
Marshal.FreeHGlobal(memCL);
}
}
else
{
// couldn't read ProcessParameters
rc = -5;
}
}
else
{
// couldn't read PEB information
rc = -4;
}
}
else
{
// PebBaseAddress is null
rc = -3;
}
}
else
{
// NtQueryInformationProcess failed
rc = -2;
}
}
finally
{
Marshal.FreeHGlobal(memPBI);
}
}
finally
{
Win32Native.CloseHandle(hProcess);
}
}
else
{
// couldn't open process for VM read
rc = -1;
}
return rc;
}
public static IReadOnlyList<string> CommandLineToArgs(string commandLine)
{
if (string.IsNullOrEmpty(commandLine)) { return new string[] { }; }
var argv = Win32Native.CommandLineToArgv(commandLine, out var argc);
if (argv == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
try
{
var args = new string[argc];
for (var i = 0; i < args.Length; ++i)
{
var p = Marshal.ReadIntPtr(argv, i * IntPtr.Size);
args[i] = Marshal.PtrToStringUni(p);
}
return args.ToList().AsReadOnly();
}
finally
{
Marshal.FreeHGlobal(argv);
}
}
}
Linux solution (32 and 64 bit can be freely mixed):
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
public static class ProcessCommandLine
{
public static int Retrieve(Process process, out string commandLine, out List<string> environment)
{
int pid = process.Id;
try
{
commandLine = File.ReadAllText("/proc/" + pid + "/cmdline").Replace('\0', ' ');
environment = File.ReadAllText("/proc/" + pid + "/environ").Split('\0').ToList();
return 0;
}
catch (Exception e)
{
commandLine = null; environment = null;
Console.WriteLine("ProcessCommandLine: Cannot read file - maybe you have to 'sudo mount -t procfs none /proc'? Maybe you have insufficient rights?");
Console.WriteLine("ProcessCommandLine: Exception was: " + e.GetType() + ": " + e.Message);
}
return -11;
}
}

translating C++/CLI code to C#

I'm trying to translate this c++/cli code to c#
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>
using namespace System;
using namespace System::Runtime::InteropServices;
struct CREDENTIAL
{
DWORD Flags;
DWORD Type;
LPSTR TargetName;
LPSTR Comment;
Runtime::InteropServices::ComTypes::FILETIME LastWritten;
DWORD CredentialBlobSize;
LPBYTE CredentialBlob;
DWORD Persist;
DWORD AttributeCount;
LPBYTE Attributes;
LPSTR TargetAlias;
LPWSTR UserName;
};
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet::Unicode)]
extern BOOL CredEnumerate(LPCTSTR Filter, DWORD Flags, DWORD* count, CREDENTIAL*** Credentials);
[DllImport("advapi32.dll")]
extern VOID CredFree(LPVOID);
int main( array<System::String^>^argv )
{
String^ credentialList = "";
Int32 count = 0;
CREDENTIAL** credentialCollection = 0;
if( CredEnumerate( _T("WindowsLive:name=*"), 0, (DWORD*)&count, &credentialCollection) )
{
for( Int32 n = 0; n < count; n++ )
{
credentialList += "\n";
credentialList += "Username " + gcnew System::String( credentialCollection[n]->UserName ) + "\n";
credentialList += "Password: " + gcnew System::String( (LPWSTR)credentialCollection[n]->CredentialBlob ) + "\n";
}
CredFree( &credentialCollection );
Console::WriteLine(credentialList);
}
Console::ReadLine();
}
Here's my c# code .. (I am not sure how CredEnumerate does work)
using System;
using System.Runtime.InteropServices; // DLL support
using System.Collections.Generic;
public struct CREDENTIAL
{
public UInt32 flags;
public UInt32 type;
public string targetName;
public string comment;
public System.Runtime.InteropServices.ComTypes.FILETIME lastWritten;
public UInt32 credentialBlobSize;
public IntPtr credentialBlob;
public UInt32 persist;
public UInt32 attributeCount;
public IntPtr credAttribute;
public string targetAlias;
public string userName;
}
class Test
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CredEnumerate(string filter, int flag, out int count, out IntPtr
pCredentials);
static void Main()
{
try
{
int count = 0;
IntPtr pCredentials = IntPtr.Zero;
IntPtr[] credentials = null;
bool ret = CredEnumerate("WindowsLive:name=*", 0, out count, out pCredentials);
if (ret != false)
{
credentials = new IntPtr[count];
IntPtr p = pCredentials;
for (int n = 0; n < count; n++)
{
if (Marshal.SizeOf(p) == 4) //32 bit CLR?
p = new IntPtr(p.ToInt32() + n);
else
p = new IntPtr(p.ToInt64() + n);
credentials[n] = Marshal.ReadIntPtr(p);
}
List<CREDENTIAL> creds = new List<CREDENTIAL>(credentials.Length);
foreach (var ptr in credentials)
{
creds.Add((CREDENTIAL)Marshal.PtrToStructure(ptr, typeof(CREDENTIAL)));
}
}
}
catch (Exception x)
{
Console.WriteLine(x.ToString());
}
Console.ReadLine();
}
}
I hope someone can help me
It's pretty scary code, I have my doubts about the CLI/C++ being right. You should look up the definitions of your structure e.g.
http://msdn.microsoft.com/en-us/library/aa374788(VS.85).aspx
Then clean-up the code properly before posting.
I would be tempted to use the code from this article on credential management
Still, that involves keeping the CLI/C++ which you seem to want to get rid of so I gave fixing it a go.
Now this works on my machine, that I've hacked it together... (note: I've removed your filter so I could test it)
[StructLayout(LayoutKind.Sequential)]
internal struct CREDENTIAL {
public int Flags;
public int Type;
[MarshalAs(UnmanagedType.LPWStr)]
public string TargetName;
[MarshalAs(UnmanagedType.LPWStr)]
public string Comment;
public long LastWritten;
public int CredentialBlobSize;
public IntPtr CredentialBlob;
public int Persist;
public int AttributeCount;
public IntPtr Attributes;
[MarshalAs(UnmanagedType.LPWStr)]
public string TargetAlias;
[MarshalAs(UnmanagedType.LPWStr)]
public string UserName;
}
Then the reading
try
{
uint count;
IntPtr pCredentials = IntPtr.Zero;
IntPtr[] credentials;
bool ret = CredEnumerateW("*", 0, out count, out pCredentials);
if (ret)
{
credentials = new IntPtr[count];
List<CREDENTIAL> creds = new List<CREDENTIAL>(credentials.Length);
for(int i = 0; i < count; i++) {
IntPtr structs= Marshal.ReadIntPtr(pCredentials, IntPtr.Size * i);
CREDENTIAL c = (CREDENTIAL)Marshal.PtrToStructure(structs, typeof(CREDENTIAL));
creds.Add(c);
}
}
}
catch (Exception x)
{
Console.WriteLine(x.ToString());
}
and
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CredEnumerateW(string filter, uint flag, out uint count, out IntPtr pCredentials);
It doesn't free the structure etc.
have a look at pinvoke.net it may have an example of how to call CredEnumerate

Categories

Resources