C#访问Win 32的一些尝试
使用C#调用Win 32 Api大部分情况下基本只涉及到参数类型的转变,但在遇到Win 32 Api返回LPVOID *lpBuff 时会遇到一些解析遍历难题。lpBuff为二维指针,*lpBuff是指向其内容的数组的首地址,在C/C++中可直接通过数组下标进行访问。但在C#中会有如下问题:
- 无法像C/C++那样进行结构体指针强制转变;
- 无法进行(*lpBuff)[0]这样的直接取值操作;
- 无法执行lptemp++这样的指针累加/累减遍历操作;
在相关文档中,可以看到C#使用IntPtr封装了指针,Marshal提供了一些快捷方法来实现*lpstruct to Struct。但是在IntPtr的遍历上依然需要一些技巧。以获取网络组的Win 32 Api为例:
Win 32 Api:
typedef struct _LOCALGROUP_INFO_1 {
LPWSTR lgrpi1_name;
LPWSTR lgrpi1_comment;
} LOCALGROUP_INFO_1, *PLOCALGROUP_INFO_1, *LPLOCALGROUP_INFO_1; NET_API_STATUS NetLocalGroupEnum(
_In_ LPCWSTR servername,
_In_ DWORD level,
_Out_ LPBYTE *bufptr,
_In_ DWORD prefmaxlen,
_Out_ LPDWORD entriesread,
_Out_ LPDWORD totalentries,
_Inout_ PDWORD_PTR resumehandle
);
To C# :
[StructLayout(LayoutKind.Sequential)]
internal struct LOCALGROUP_INFO_1
{
[MarshalAs(UnmanagedType.LPWStr)]
public string lgrpi1_name;
[MarshalAs(UnmanagedType.LPWStr)]
public string lgrpi1_comment; }
DllImport("Netapi32.dll",CharSet = CharSet.Unicode, SetLastError = true)]
internal extern static uint NetLocalGroupEnum([MarshalAs(UnmanagedType.LPWStr)] string servername, int level, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, ref int resume_handle);
该函数获取本地机器上的所有网络组,组结构包含了组的名字以及组的描述。 NetLocalGroupEnum函数内部分配了bufptr,返回给调用者。GetGroupList的C#代码如下:
public static IEnumerable<dynamic> GetGroupList(this Vpn v) //函数代码基本上copy自[8]
{
int size = ; //Start with 4k
var bufptr = new IntPtr(size);
int level = ;
int prefmaxlen = ;
int entriesread = ;
int totalentries = ;
int resume_handle = ;
uint err = ;
do
{
err = NetLocalGroupEnum(
null,
level,
out bufptr,
prefmaxlen,
out entriesread,
out totalentries,
ref resume_handle
);
switch (err)
{
//If there is more data, double the size of the buffer...
case NERR_BufTooSmall: //NERR_BufTooSmall
case ERROR_MORE_DATA: //ERROR_MORE_DATA
size *= ;
bufptr = new IntPtr(size);
prefmaxlen = size - ; //Increase the size you want read as well
resume_handle = ; //And reset the resume_handle or you'll just pick up where you left off.
break;
case : //NERR_InvalidComputer
throw new Exception("NERR_InvalidComputer");
case : //NERR_Success
default:
break;
}
}
while (err == ERROR_MORE_DATA); // and start over //GROUP_INFO_0 group=new GROUP_INFO_0(); //See user type above
LOCALGROUP_INFO_1 group;
var glist = new List<dynamic>(entriesread);
var iter = bufptr; for (int i = ; i < entriesread; i++)
{
group = Marshal.PtrToStructure<LOCALGROUP_INFO_1>(iter);
glist.Add(new { Name = group.lgrpi1_name,
Commet = group.lgrpi1_comment});
iter = (IntPtr)(iter.ToInt64() + Marshal.SizeOf<LOCALGROUP_INFO_1>());
} NetApiBufferFree(bufptr);
return glist;
}
其中 group = Marshal.PtrToStructure<LOCALGROUP_INFO_1>(iter); 负责将byte[]转换为结构体;iter = (IntPtr)(iter.ToInt64() + Marshal.SizeOf<LOCALGROUP_INFO_1>()); 负责遍历。
在遍历过程中,iter = (IntPtr)(iter.ToInt64() + Marshal.SizeOf<LOCALGROUP_INFO_1>())中获取获取指针数组基地址的函数是iter.ToInt64() 。不论把编译设置为X86,还是X64,ToInt64和ToInt32在任何平台下都可以正常使用。但是ToInt32在64位系统中,可能会溢出。ToInt32与ToInt64与具体的指针类型(Int32* or Int64*)无关,它只是一个逻辑整数,是其基地址的值。IntPtr还有一个扩展方法ToInt(),经测试无法用来遍历。
如上即可进行Win32 API返回缓冲区的遍历,但经过几次测试,发现该方法工作并不稳定。当返回的GROUP_INFO_1实例中存在Comment = NULL时,内存读取会出错。出错时,可能是读取NULL出错;也可能是读取到NULL之后,进行下一次遍历时出错。考虑到可能声明为[MarshalAs(UnmanagedType.LPWStr)]存在出错可能,决定仿照其他一些写法,将其字符串指针LPCWSTR声明为IntPtr进行处理。
改变如下:
[StructLayout(LayoutKind.Sequential)]
internal struct LOCALGROUP_INFO_1
{
//[MarshalAs(UnmanagedType.LPWStr)]
//public string grpi1_name;
public IntPtr grpi1_name;
//[MarshalAs(UnmanagedType.LPWStr)]
//public string grpi1_comment;
public IntPtr grpi1_comment;
}
//解析
for (int i = ; i < entriesread; i++)
{
group = Marshal.PtrToStructure<LOCALGROUP_INFO_1>(iter);
glist.Add(new { Name = Marshal.PtrToStringUni(group.grpi1_name),
Commet = Marshal.PtrToStringUni(group.grpi1_comment)});
iter = (IntPtr)(iter.ToInt64() + Marshal.SizeOf<LOCALGROUP_INFO_1>());
}
依然出错,但有好转。entriesread返回当前存在19个组,经查看windows【管理】确实也存在19个组。但与LPWSTR定义一样,当在第9个组出现comment = null之后,接下来的所有组Name & Comment均为null,继而在第18个记录时报错。从Windows的用户组看来,所有的组都存在描述,而遍历时却找不到描述信息,这确实很奇怪。
通过仔细查看遍历记录,发现遍历全部为用户名,而没有描述。突然想到是否是用户组信息的类别出错,果然,错误第使用了样例代码的Level 0,而应该将Level设置为1。设置为1之后,在原代码上工作不正确,提示需要更多内存。修改原代码后能正确工作,并指出其中的一个错误。(示例代码来自[8])
public static IEnumerable<dynamic> GetGroupList(this Vpn v)
{
var bufptr = IntPtr.Zero;// 在[8]中,此处new IntPtr(size)为Intptr的默认句柄值,而不是缓冲区大小。在内存不足时,不是也不能通过扩大Size来增加内存。prefmaxlen才是缓冲区的大小。
uint level = ;
uint prefmaxlen = MAX_PREFERRED_LENGTH; //此处可以设置为最大,并不需要太担心内存问题。这是个最大接收值,内存Win 32是动态创建的。*除非你不想一次获取这么多数据。
uint entriesread = ;
uint totalentries = ;
uint resume_handle = ;
uint err = ;
err = NetLocalGroupEnum(
null,
level,
ref bufptr,
prefmaxlen,
ref entriesread,
ref totalentries,
ref resume_handle
); LOCALGROUP_INFO_1 group;
var glist = new List<dynamic>(entriesread.ToInt());
var iter = bufptr; for (int i = ; i < entriesread; i++)
{
group = Marshal.PtrToStructure<LOCALGROUP_INFO_1>(iter);
glist.Add(new { Name = group.lgrpi1_name,
Comment = group.lgrpi1_comment});
iter = (IntPtr)(iter.ToInt64() + Marshal.SizeOf<LOCALGROUP_INFO_1>());
} NetApiBufferFree(bufptr);
return glist;
}
工作正确,输出如下:
Access Control Assistance Operators, 此组的成员可以远程查询此计算机上资源的授权属性和权限。
Administrators, 管理员对计算机/域有不受限制的完全访问权
Backup Operators, 备份操作员为了备份或还原文件可以替代安全限制
Cryptographic Operators, 授权成员执行加密操作。
Distributed COM Users, 成员允许启动、激活和使用此计算机上的分布式 COM 对象。
Event Log Readers, 此组的成员可以从本地计算机中读取事件日志
Guests, 按默认值,来宾跟用户组的成员有同等访问权,但来宾帐户的限制更多
Hyper-V Administrators, 此组的成员拥有对 Hyper-V 所有功能的完全且不受限制的访问权限。
IIS_IUSRS, Internet 信息服务使用的内置组。
Network Configuration Operators, 此组中的成员有部分管理权限来管理网络功能的配置
Performance Log Users, 该组中的成员可以计划进行性能计数器日志记录、启用跟踪记录提供程序,以及在本地或通过远程访问此计算 机来收集事件跟踪记录
Performance Monitor Users, 此组的成员可以从本地和远程访问性能计数器数据
Power Users, 包括高级用户以向下兼容,高级用户拥有有限的管理权限
Remote Desktop Users, 此组中的成员被授予远程登录的权限
Remote Management Users, 此组的成员可以通过管理协议(例如,通过 Windows 远程管理服务实现的 WS-Management)访问 WMI 资源。 这仅适用于授予用户访问权限的 WMI 命名空间。
Replicator, 支持域中的文件复制
System Managed Accounts Group, 此组的成员由系统管理。
Users, 防止用户进行有意或无意的系统范围的更改,但是可以运行大部分应用程序
__vmware__, VMware User Group
参考
[1] https://msdn.microsoft.com/zh-cn/library/fzhhdwae%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
[2] https://msdn.microsoft.com/zh-cn/library/system.intptr.subtract(v=vs.110).aspx
[3] 指针类型, https://msdn.microsoft.com/zh-cn/library/aa664771(v=vs.71).aspx
[4] 指针类型(C#编程指南), https://msdn.microsoft.com/zh-cn/library/y31yhkeb.aspx
[5] 类型支持, https://msdn.microsoft.com/zh-cn/library/c300dbaa(v=vs.90).aspx
[6] 平台调用教程, https://msdn.microsoft.com/zh-cn/library/aa288468(v=vs.71).aspx
[7] a wiki for .net developers, http://www.pinvoke.net/
[8]NetLocalGroupEnum, http://www.pinvoke.net/default.aspx/netapi32.NetLocalGroupEnum
[9] NetLocalGroupEnum, https://msdn.microsoft.com/en-us/library/windows/desktop/aa370440(v=vs.85).aspx
C#访问Win 32的一些尝试的更多相关文章
- (转)C#调用非托管Win 32 DLL
转载学习收藏,原文地址http://www.cnblogs.com/mywebname/articles/2291876.html 背景 在项目过程中,有时候你需要调用非C#编写的DLL文件,尤其在使 ...
- 启动django应用报错 “Error: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试。”
启动django应用时报如下错误 "Error: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试." 网上查了一下,是8000端口被其他程序占 ...
- apache(OS 10013)以一种访问权限不允许的方式做了一个访问套接字的尝试 ...
今天启动Apache时, 报了“(OS 10013)以一种访问权限不允许的方式做了一个访问套接字的尝试. : make_sock: could not bind to address 0.0.0.0: ...
- HeadFirstPython学习笔记——OSError: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试。
1.文件构成如下 2.运行服务器时报错 OSError: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试. 解决方法:更换端口 3.Python的CGI跟踪术 在 ...
- 未经处理的异常:System.Net.Sockets.SocketException: 以一种访问权限不允许的方式做了一个访问套接字的尝试
报错:未经处理的异常:System.Net.Sockets.SocketException: 以一种访问权限不允许的方式做了一个访问套接字的尝试 → 尝试以"管理员身份"运行程序, ...
- 【django】Error: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试。
问题描述:启动django服务时出现“Error: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试.”的错误 问题原因:8000端口被占用了 解决办法:默认启动的 ...
- 10013: 以一种访问权限不允许的方式做了一个访问套接字的尝试【WCF异常】
错误代码:10013 异常描述:侦听 IP 终结点=0.0.0.0:6666 时出现 TCP 错误(10013: 以一种访问权限不允许的方式做了一个访问套接字的尝试.). 解决方式:由于端口6666被 ...
- eclipse svn 以一种访问权限不允许的方式做了一个访问套接字的尝试
以一种访问权限不允许的方式做了一个访问套接字的尝试 svn: Unable to connect http://xxx.xxx 安装插件是把Eclipse的网络访问禁止了,然后用svn就老提示[以一种 ...
- UnityError SocketException: 以一种访问权限不允许的方式做了一个访问套接字的尝试。
SocketException: 以一种访问权限不允许的方式做了一个访问套接字的尝试. 以管理员身份运行Unity就可以了,权限不够的问题.
随机推荐
- (问题待解决)sql查询不显示前置‘0’的问题
SELECT * , ,),() ) ' end)) from CourseBooksNotes ),)),)+'秒'; ),)),)),)+'秒'
- [BZOJ 4999]This Problem Is Too Simple!
[BZOJ 4999]This Problem Is Too Simple! 题目 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将 ...
- 运维系列之一 Linux的文件与目录权限解析
在Linux中,万事万物皆文件,普通文件是文件,目录是文件,硬件设备也是文件,因此学习了解Linux中的文件非常重要. Linux中有三种文件类型: (1) 普通文件:又分为文本文件和二进制文件 (2 ...
- POJ River Hopscotch 二分搜索
Every year the cows hold an event featuring a peculiar version of hopscotch that involves carefully ...
- OceanBase架构浅析(一)
简介 OceanBase是阿里集团研发的可扩展的关系数据库,实现了数千亿条记录.数百TB数据上的跨行跨表事务,截止到2012年8月,支持了收藏夹.直通车报表.天猫评价等OLTP和OLAP在线业务,线上 ...
- Unity图片变灰的方式
http://www.tuicool.com/articles/Vruuqme NGUI中的Button差点儿是最经常使用到的控件之中的一个,而且能够组合各种组件(比方UIButtonColor,UI ...
- Spring MVC新手教程(一)
直接干货 model 考虑给用户展示什么.关注支撑业务的信息构成.构建成模型. control 调用业务逻辑产生合适的数据以及传递数据给视图用于呈献: view怎样对数据进行布局,以一种优美的方式展示 ...
- 【Linux】Linux下配置apache
一.获取软件: http://httpd.apache.org/ httpd-2.4.10.tar.gz 二.安装步骤: 解压源文件: 1) tar zvxf httpd-2.4.10.tar. ...
- jQuery - 制作非缘勿扰页面特效
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- JS容易犯错的this和作用域
var someuser = { name: 'byvoid', func: function() { console.log(this.name); } }; var foo = { name: ' ...