去年我开发了一个软件,需要枚举COM接口,而我以前使用vb6时使用了API来完成。但现在使用VS2003,其并没有这方面的类。因此我还是使用API来完成。下面是代码:
using
System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace System.Shangfei.Controls.SerialPort
... {
/**//// <summary>
/// EnumPort 的摘要说明。
/// </summary>
public class EnumPort
...{
public ArrayList Ports=new ArrayList();
"Dll定义"#region "Dll定义"
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
struct PORT_INFO_1
...{
[MarshalAs(UnmanagedType.LPTStr)]
public string pName;
}
[DllImport("winspool.drv", CharSet=CharSet.Auto)]
static extern bool EnumPorts(string pName, int level, IntPtr bufptr,
int cbBuf, out int pcbNeeded, out int pcReturned);
#endregion
public EnumPort()
...{
//
// TODO: 在此处添加构造函数逻辑
//
ListPorts(null);
}
public EnumPort(string Prx)
...{
//
// TODO: 在此处添加构造函数逻辑
//
ListPorts(Prx);
}
public void Dispose()
...{
Ports.Clear();
}
/**//// <summary>
/// 搜索系统的端口,并放在Ports数组列表中
/// </summary>
/// <param name="Prx">过滤字符串</param>
private void ListPorts(string Prx)
...{
int pcReturned = 0;
int pcbNeeded = 0;
IntPtr outb = IntPtr.Zero;
EnumPorts(null, 1, outb, 0, out pcbNeeded, out pcReturned);
outb = Marshal.AllocHGlobal(pcbNeeded+1);
EnumPorts(null, 1, outb, pcbNeeded, out pcbNeeded, out pcReturned);
PORT_INFO_1[] portsArray = new PORT_INFO_1[pcReturned];
IntPtr current = outb;
for (int i=0; i<pcReturned; i++)
...{
portsArray[i] = (PORT_INFO_1)Marshal.PtrToStructure(current,typeof(PORT_INFO_1));
current=(IntPtr)((int)current+Marshal.SizeOf(typeof(PORT_INFO_1)));
if (Prx!=null)
...{
if (portsArray[i].pName.StartsWith(Prx.ToUpper() ))
Ports.Add(new string(portsArray[i].pName.ToCharArray()));
}
else
...{Ports.Add(new string(portsArray[i].pName.ToCharArray()));}
}
for (int i=0; i<Ports.Count ; i++)
...{
if (Ports[i].ToString().EndsWith(":"))
Ports[i]=Ports[i].ToString().Substring(0,Ports[i].ToString().Length-1);
}
Marshal.FreeHGlobal(outb);
}
}//class
}
using System.Collections;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace System.Shangfei.Controls.SerialPort
... {
/**//// <summary>
/// EnumPort 的摘要说明。
/// </summary>
public class EnumPort
...{
public ArrayList Ports=new ArrayList();
"Dll定义"#region "Dll定义"
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
struct PORT_INFO_1
...{
[MarshalAs(UnmanagedType.LPTStr)]
public string pName;
}
[DllImport("winspool.drv", CharSet=CharSet.Auto)]
static extern bool EnumPorts(string pName, int level, IntPtr bufptr,
int cbBuf, out int pcbNeeded, out int pcReturned);
#endregion
public EnumPort()
...{
//
// TODO: 在此处添加构造函数逻辑
//
ListPorts(null);
}
public EnumPort(string Prx)
...{
//
// TODO: 在此处添加构造函数逻辑
//
ListPorts(Prx);
}
public void Dispose()
...{
Ports.Clear();
}
/**//// <summary>
/// 搜索系统的端口,并放在Ports数组列表中
/// </summary>
/// <param name="Prx">过滤字符串</param>
private void ListPorts(string Prx)
...{
int pcReturned = 0;
int pcbNeeded = 0;
IntPtr outb = IntPtr.Zero;
EnumPorts(null, 1, outb, 0, out pcbNeeded, out pcReturned);
outb = Marshal.AllocHGlobal(pcbNeeded+1);
EnumPorts(null, 1, outb, pcbNeeded, out pcbNeeded, out pcReturned);
PORT_INFO_1[] portsArray = new PORT_INFO_1[pcReturned];
IntPtr current = outb;
for (int i=0; i<pcReturned; i++)
...{
portsArray[i] = (PORT_INFO_1)Marshal.PtrToStructure(current,typeof(PORT_INFO_1));
current=(IntPtr)((int)current+Marshal.SizeOf(typeof(PORT_INFO_1)));
if (Prx!=null)
...{
if (portsArray[i].pName.StartsWith(Prx.ToUpper() ))
Ports.Add(new string(portsArray[i].pName.ToCharArray()));
}
else
...{Ports.Add(new string(portsArray[i].pName.ToCharArray()));}
}
for (int i=0; i<Ports.Count ; i++)
...{
if (Ports[i].ToString().EndsWith(":"))
Ports[i]=Ports[i].ToString().Substring(0,Ports[i].ToString().Length-1);
}
Marshal.FreeHGlobal(outb);
}
}//class
}
在使用过程中也正常,没有发现问题。但是心里总有些不舒服,我不是很喜欢在C#环境中加入API。前段时间我使用Reflector查看了VS2005中SerialPort类,在其中有一个EnumPort类可以实现枚举COM接口。其使用访问注册表来实现此功能,我立即将其代码移到VS2003中,发现运行正常。终于去了我一块心病。
public
string
[] ListCOMPorts()
... {
RegistryKey localMachine = null;
RegistryKey key2 = null;
string[] strArray = null;
new RegistryPermission(RegistryPermissionAccess.Read, @"HKEY_LOCAL_MACHINEHARDWAREDEVICEMAPSERIALCOMM").Assert();
try
...{
localMachine = Registry.LocalMachine;
key2 = localMachine.OpenSubKey(@"HARDWAREDEVICEMAPSERIALCOMM", false);
if (key2 != null)
...{
string[] valueNames = key2.GetValueNames();
strArray = new string[valueNames.Length];
for (int i = 0; i < valueNames.Length; i++)
...{
strArray[i] = (string) key2.GetValue(valueNames[i]);
}
}
}
finally
...{
if (localMachine != null)
...{
localMachine.Close();
}
if (key2 != null)
...{
key2.Close();
}
CodeAccessPermission.RevertAssert();
}
if (strArray == null)
...{
strArray = new string[0];
}
return strArray;
}
... {
RegistryKey localMachine = null;
RegistryKey key2 = null;
string[] strArray = null;
new RegistryPermission(RegistryPermissionAccess.Read, @"HKEY_LOCAL_MACHINEHARDWAREDEVICEMAPSERIALCOMM").Assert();
try
...{
localMachine = Registry.LocalMachine;
key2 = localMachine.OpenSubKey(@"HARDWAREDEVICEMAPSERIALCOMM", false);
if (key2 != null)
...{
string[] valueNames = key2.GetValueNames();
strArray = new string[valueNames.Length];
for (int i = 0; i < valueNames.Length; i++)
...{
strArray[i] = (string) key2.GetValue(valueNames[i]);
}
}
}
finally
...{
if (localMachine != null)
...{
localMachine.Close();
}
if (key2 != null)
...{
key2.Close();
}
CodeAccessPermission.RevertAssert();
}
if (strArray == null)
...{
strArray = new string[0];
}
return strArray;
}
不过,此方式目前只实现了COM的枚举,没有LPT等接口的。而采用API方式可以枚举出所有接口。另外我在网上看到采用WMI(System.Management命名空间中)提供的功能实现COM的枚举,这个我不清楚它在Window 98环境下是否可以工作,我没有Window 98系统环境,不能测试,如果有那位朋友知道,方便的话可以告诉我。