从Delphi程序发出Netsh命令

从Delphi程序发出Netsh命令,delphi,delphi-xe,netsh,Delphi,Delphi Xe,Netsh,我正在尝试捕获AirCard的设备ID。我使用以下代码的目的是将结果存储在一个文本文件(imei.txt)中,我将其存储在Temp文件夹中,并循环浏览内容,查找设备ID 问题在于它只将“以下命令未找到:mbn show interface.”写入文件 我已经从命令行测试了Netsh命令,它返回了我所期望的结果 xs1 := CreateOleObject('WSCript.Shell'); xs1.run('%comspec% /c netsh mbn show interfac

我正在尝试捕获AirCard的设备ID。我使用以下代码的目的是将结果存储在一个文本文件(imei.txt)中,我将其存储在Temp文件夹中,并循环浏览内容,查找设备ID

问题在于它只将“以下命令未找到:mbn show interface.”写入文件

我已经从命令行测试了Netsh命令,它返回了我所期望的结果

    xs1 := CreateOleObject('WSCript.Shell');
    xs1.run('%comspec% /c netsh mbn show interface > "' + IMEIFileName +
      '"', 0, true);
无法正确处理NetSh命令。我是否正确地通过了Comspec?它似乎没有运行“NetSh”命令,就像我在命令提示符下运行“mbn”一样

谢谢

unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Win.ComObj, ShlObj,     Vcl.StdCtrls;

type
  TfrmMain = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    procedure GetAirCardInformation;
    { Private declarations }
  public
    { Public declarations }
    IMEI: string;
    PhoneNumber: string;
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

procedure TfrmMain.Button1Click(Sender: TObject);
begin
  GetAirCardInformation;
end;

procedure TfrmMain.GetAirCardInformation;
var
  xs1 : OleVariant;
  IMEIFileName: String;
  IMEIStrings: TStringList;
  I: Integer;

  function GetSpecialFolder(const CSIDL: Integer): string;
  var
    RecPath: PWideChar;
  begin
    RecPath := StrAlloc(MAX_PATH);
    try
      FillChar(RecPath^, MAX_PATH, 0);
      if SHGetSpecialFolderPath(0, RecPath, CSIDL, false) then
        result := RecPath
      else
        result := '';
    finally
      StrDispose(RecPath);
    end;
  end;

begin
  IMEI := '';
  IMEIFileName := GetSpecialFolder(CSIDL_LOCAL_APPDATA) + '\Temp\imei.txt';
  Memo1.Lines.Add('IMEIFileName: ' + IMEIFileName);
  try
    if FileExists(IMEIFileName) then
      DeleteFile(IMEIFileName);

    xs1 := CreateOleObject('WSCript.Shell');
    xs1.run('%comspec% /c netsh mbn show interface > "' + IMEIFileName +
      '"', 0, true);

    if FileExists(IMEIFileName) then
    begin
      IMEIStrings := TStringList.Create;
      IMEIStrings.LoadFromFile(IMEIFileName);
      IMEIStrings.NameValueSeparator := ':';
      Memo1.Lines.Add('IMEIStrings Count: ' + intToStr(IMEIStrings.Count));
      for I := 0 to IMEIStrings.Count - 1 do
      begin
        Memo1.Lines.Add(IMEIStrings.text);
        if (Uppercase(Trim(IMEIStrings.Names[I])) = 'DEVICE ID') then
        begin
          IMEI := Trim(IMEIStrings.Values[IMEIStrings.Names[I]]);
          Memo1.Lines.Add('IMEI:' + IMEI);
          break;
        end;
      end;
    end;

  except
    IMEI := '';
  end;
  Memo1.Lines.Add('process complete');
end;

end.

您不应该使用WShell COM对象来运行
cmd.exe
。那太过分了。您可以改用
CreateProcess()
。但是,当以编程方式运行
cmd.exe
时,不能使用
运算符重定向其输出,该运算符仅在实际命令窗口中起作用。您可以改为使用
STARTUPINFO
结构将输出重定向到使用
CreatePipe()
创建的匿名管道,然后使用
ReadFile()
从该管道读取。根本不需要使用临时文件。MSDN有一篇关于此主题的文章:

在Delphi中,有大量的示例演示了这种技术

也就是说,一个更好的选择是根本不使用
netsh
。Windows 7及更高版本有一个。您可以在代码中直接枚举MBN接口

例如,使用函数:

unit WwApi;

{$MINENUMSIZE 4}

interface

uses
  Windows;

const
  WWAN_STR_DESC_LENGTH = 256;

type
  WWAN_INTERFACE_STATE = (
    WwanInterfaceStateNotReady,
    WwanInterfaceStateDeviceLocked,
    WwanInterfaceStateUserAccountNotActivated,
    WwanInterfaceStateRegistered,
    WwanInterfaceStateRegistering,
    WwanInterfaceStateDeregistered,
    WwanInterfaceStateAttached,
    WwanInterfaceStateAttaching,
    WwanInterfaceStateDetaching,
    WwanInterfaceStateActivated,
    WwanInterfaceStateActivating,
    WwanInterfaceStateDeactivating
  );

  WWAN_INTF_OPCODE = (
    WwanIntfOpcodePin,
    WwanIntfOpcodeRadioState,
    WwanIntfOpcodePreferredProviders,
    WwanIntfOpcodeCurrentConnection,
    WwanIntfOpcodeProvisionedContexts,
    WwanIntfOpcodeActivateUserAccount,
    WwanIntfOpcodeVendorSpecific,
    WwanIntfOpcodeInterfaceObject,
    WwanIntfOpcodeConnectionObject,
    WwanIntfOpcodeAcState,
    WwanIntfOpcodeClearManualConnectState,
    WwanIntfOpcodeGetStoredRadioState,
    WwanIntfOpcodeGetRadioInfo,
    WwanIntfOpcodeHomeProvider
  );

  // I don't know the definition of this type!
  WWAN_STATUS = DWORD; //?

  WWAN_INTERFACE_STATUS = record
    fInitialized: BOOL;
    InterfaceState: WWAN_INTERFACE_STATE;
  end;

  PWWAN_INTERFACE_INFO = ^WWAN_INTERFACE_INFO;
  WWAN_INTERFACE_INFO = record
    InterfaceGuid: TGuid;
    strInterfaceDescription: array[0..WWAN_STR_DESC_LENGTH-1] of WCHAR;
    InterfaceStatus: WWAN_INTERFACE_STATUS;
    ParentInterfaceGuid: TGuid;
    fIsAdditionalPdpContextInterface: BOOL;
  end;

  PWWAN_INTERFACE_INFO_LIST = ^WWAN_INTERFACE_INFO_LIST;
  WWAN_INTERFACE_INFO_LIST = record
    dwNumberOfItems: DWORD;
    pInterfaceInfo: array[0..0] of WWAN_INTERFACE_INFO;
  end;

function WwanOpenHandle(dwClientVersion: DWORD; pReserved: Pointer; var pdwNegotiatedVersion: DWORD; var phClientHandle: THandle): DWORD; stdcall;
function WwanCloseHandle(hClientHandle: THandle; pReserved: Pointer): DWORD; stdcall;
function WwanEnumerateInterfaces(hClientHandle: THandle; pdwReserved: PDWORD; var ppInterfaceList: PWWAN_INTERFACE_INFO_LIST): DWORD; stdcall;
procedure WwanFreeMemory(pMem: Pointer); stdcall;
function WwanQueryInterface(hClientHandle: THandle; const pInterfaceGuid: TGuid; opCode: WWAN_INTF_OPCODE; pReserved: Pointer; var pdwDataSize: DWORD; var ppData: PByte; var pRequestId: ULONG; var pStatus: WWAN_STATUS): DWORD; stdcall;

implementation

const
  WwApiLib = 'WwApi.dll';

function WwanOpenHandle; external WwApiLib delayed;
function WwanCloseHandle; external WwApiLib delayed;
function WwanEnumerateInterfaces; external WwApiLib delayed;
procedure WwanFreeMemory; external WwApiLib delayed;
function WwanQueryInterface; external WwApiLib delayed;

end.

或者,使用和COM接口,这将为您提供更详细的信息:

unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.StdCtrls;

type
  TfrmMain = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    procedure GetAirCardInformation;
    { Private declarations }
  public
    { Public declarations }
    IMEI: string;
    PhoneNumber: string;
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

uses
  // I found the MbnApi.pas unit on the DelphiPraxis forum:
  //
  // http://www.delphipraxis.net/1342330-post2.html
  //
  // It is too large to post here on StackOverflow!
  // Otherwise, you can import the mbnapi.tlb TypeLibrary yourself...
  //
  MbnApi, ActiveX, ComObj;

procedure TfrmMain.Button1Click(Sender: TObject);
begin
  GetAirCardInformation;
end;

procedure TfrmMain.GetAirCardInformation;
var
  Mgr: IMbnInterfaceManager;
  pInterfaceArray, pPhoneNumberArray: PSafeArray;
  pInterface: IMbnInterface;
  subscriber: IMbnSubscriberInformation;
  ReadyState: MBN_READY_STATE;
  lIntfLower, lIntfUpper: LONG;
  lPhoneNumLower, lPhoneNumUpper: LONG;
  I, J: LONG;
  wStr: WideString;
begin
  Memo1.Clear;
  try
    OleCheck(CoCreateInstance(CLASS_MbnInterfaceManager, nil, CLSCTX_ALL, IMbnInterfaceManager, Mgr));
    OleCheck(Mgr.GetInterfaces(pInterfaceArray));
    try
      OleCheck(SafeArrayGetLBound(pInterfaceArray, 1, lIntfLower));
      OleCheck(SafeArrayGetUBound(pInterfaceArray, 1, lIntfUpper));
      for I = lIntfLower to lIntfUpper do
      begin
        OleCheck(SafeArrayGetElement(pInterfaceArray, I, pInterface));
        try
          // use pInterface as needed...

          OleCheck(pInterface.get_InterfaceID(wStr));
          try
            Memo1.Lines.Add('Interface ID:' + wStr);
          finally
            wStr := '';
          end;

          OleCheck(pInterface.GetReadyState(ReadyState));
          Memo1.Lines.Add('Ready State:' + IntToStr(Ord(ReadyState)));

          OleCheck(pInterface.GetSubscriberInformation(subscriber));
          try
            OleCheck(subscriber.Get_SubscriberID(wStr));
            try
              Memo1.Lines.Add('Subscriber ID: ' + wStr);
            finally
              wStr := '';
            end;

            OleCheck(subscriber.Get_SimIccID(wStr));
            try
              Memo1.Lines.Add('Sim ICC ID: ' + wStr);
            finally
              wStr := '';
            end;

            OleCheck(subscriber.Get_TelephoneNumbers(pPhoneNumberArray));
            try
              OleCheck(SafeArrayGetLBound(pPhoneNumberArray, 1, lPhoneNumLower));
              OleCheck(SafeArrayGetUBound(pPhoneNumberArray, 1, lPhoneNumUpper));
              for J = lPhoneNumLower to lPhoneNumUpper do
              begin
                OleCheck(SafeArrayGetElement(pPhoneNumberArray, J, wStr));
                try
                  Memo1.Lines.Add('Phone #:' + wStr);
                finally
                  wStr := '';
                end;
              end;
            finally
              SafeArrayDestroy(pPhoneNumberArray);
            end;
          finally
            subscriber := nil;
          end;

          // and so on...

          Memo1.Lines.Add('');
        finally
          pInterface := nil;
        end;
      end;
    finally
      SafeArrayDestroy(pInterfaceArray);
    end;
  except
  end;

  Memo1.Lines.Add('process complete');
end;

end.

您不应该使用WShell COM对象来运行
cmd.exe
。那太过分了。您可以改用
CreateProcess()
。但是,当以编程方式运行
cmd.exe
时,不能使用
运算符重定向其输出,该运算符仅在实际命令窗口中起作用。您可以改为使用
STARTUPINFO
结构将输出重定向到使用
CreatePipe()
创建的匿名管道,然后使用
ReadFile()
从该管道读取。根本不需要使用临时文件。MSDN有一篇关于此主题的文章:

在Delphi中,有大量的示例演示了这种技术

也就是说,一个更好的选择是根本不使用
netsh
。Windows 7及更高版本有一个。您可以在代码中直接枚举MBN接口

例如,使用函数:

unit WwApi;

{$MINENUMSIZE 4}

interface

uses
  Windows;

const
  WWAN_STR_DESC_LENGTH = 256;

type
  WWAN_INTERFACE_STATE = (
    WwanInterfaceStateNotReady,
    WwanInterfaceStateDeviceLocked,
    WwanInterfaceStateUserAccountNotActivated,
    WwanInterfaceStateRegistered,
    WwanInterfaceStateRegistering,
    WwanInterfaceStateDeregistered,
    WwanInterfaceStateAttached,
    WwanInterfaceStateAttaching,
    WwanInterfaceStateDetaching,
    WwanInterfaceStateActivated,
    WwanInterfaceStateActivating,
    WwanInterfaceStateDeactivating
  );

  WWAN_INTF_OPCODE = (
    WwanIntfOpcodePin,
    WwanIntfOpcodeRadioState,
    WwanIntfOpcodePreferredProviders,
    WwanIntfOpcodeCurrentConnection,
    WwanIntfOpcodeProvisionedContexts,
    WwanIntfOpcodeActivateUserAccount,
    WwanIntfOpcodeVendorSpecific,
    WwanIntfOpcodeInterfaceObject,
    WwanIntfOpcodeConnectionObject,
    WwanIntfOpcodeAcState,
    WwanIntfOpcodeClearManualConnectState,
    WwanIntfOpcodeGetStoredRadioState,
    WwanIntfOpcodeGetRadioInfo,
    WwanIntfOpcodeHomeProvider
  );

  // I don't know the definition of this type!
  WWAN_STATUS = DWORD; //?

  WWAN_INTERFACE_STATUS = record
    fInitialized: BOOL;
    InterfaceState: WWAN_INTERFACE_STATE;
  end;

  PWWAN_INTERFACE_INFO = ^WWAN_INTERFACE_INFO;
  WWAN_INTERFACE_INFO = record
    InterfaceGuid: TGuid;
    strInterfaceDescription: array[0..WWAN_STR_DESC_LENGTH-1] of WCHAR;
    InterfaceStatus: WWAN_INTERFACE_STATUS;
    ParentInterfaceGuid: TGuid;
    fIsAdditionalPdpContextInterface: BOOL;
  end;

  PWWAN_INTERFACE_INFO_LIST = ^WWAN_INTERFACE_INFO_LIST;
  WWAN_INTERFACE_INFO_LIST = record
    dwNumberOfItems: DWORD;
    pInterfaceInfo: array[0..0] of WWAN_INTERFACE_INFO;
  end;

function WwanOpenHandle(dwClientVersion: DWORD; pReserved: Pointer; var pdwNegotiatedVersion: DWORD; var phClientHandle: THandle): DWORD; stdcall;
function WwanCloseHandle(hClientHandle: THandle; pReserved: Pointer): DWORD; stdcall;
function WwanEnumerateInterfaces(hClientHandle: THandle; pdwReserved: PDWORD; var ppInterfaceList: PWWAN_INTERFACE_INFO_LIST): DWORD; stdcall;
procedure WwanFreeMemory(pMem: Pointer); stdcall;
function WwanQueryInterface(hClientHandle: THandle; const pInterfaceGuid: TGuid; opCode: WWAN_INTF_OPCODE; pReserved: Pointer; var pdwDataSize: DWORD; var ppData: PByte; var pRequestId: ULONG; var pStatus: WWAN_STATUS): DWORD; stdcall;

implementation

const
  WwApiLib = 'WwApi.dll';

function WwanOpenHandle; external WwApiLib delayed;
function WwanCloseHandle; external WwApiLib delayed;
function WwanEnumerateInterfaces; external WwApiLib delayed;
procedure WwanFreeMemory; external WwApiLib delayed;
function WwanQueryInterface; external WwApiLib delayed;

end.

或者,使用和COM接口,这将为您提供更详细的信息:

unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.StdCtrls;

type
  TfrmMain = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    procedure GetAirCardInformation;
    { Private declarations }
  public
    { Public declarations }
    IMEI: string;
    PhoneNumber: string;
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

uses
  // I found the MbnApi.pas unit on the DelphiPraxis forum:
  //
  // http://www.delphipraxis.net/1342330-post2.html
  //
  // It is too large to post here on StackOverflow!
  // Otherwise, you can import the mbnapi.tlb TypeLibrary yourself...
  //
  MbnApi, ActiveX, ComObj;

procedure TfrmMain.Button1Click(Sender: TObject);
begin
  GetAirCardInformation;
end;

procedure TfrmMain.GetAirCardInformation;
var
  Mgr: IMbnInterfaceManager;
  pInterfaceArray, pPhoneNumberArray: PSafeArray;
  pInterface: IMbnInterface;
  subscriber: IMbnSubscriberInformation;
  ReadyState: MBN_READY_STATE;
  lIntfLower, lIntfUpper: LONG;
  lPhoneNumLower, lPhoneNumUpper: LONG;
  I, J: LONG;
  wStr: WideString;
begin
  Memo1.Clear;
  try
    OleCheck(CoCreateInstance(CLASS_MbnInterfaceManager, nil, CLSCTX_ALL, IMbnInterfaceManager, Mgr));
    OleCheck(Mgr.GetInterfaces(pInterfaceArray));
    try
      OleCheck(SafeArrayGetLBound(pInterfaceArray, 1, lIntfLower));
      OleCheck(SafeArrayGetUBound(pInterfaceArray, 1, lIntfUpper));
      for I = lIntfLower to lIntfUpper do
      begin
        OleCheck(SafeArrayGetElement(pInterfaceArray, I, pInterface));
        try
          // use pInterface as needed...

          OleCheck(pInterface.get_InterfaceID(wStr));
          try
            Memo1.Lines.Add('Interface ID:' + wStr);
          finally
            wStr := '';
          end;

          OleCheck(pInterface.GetReadyState(ReadyState));
          Memo1.Lines.Add('Ready State:' + IntToStr(Ord(ReadyState)));

          OleCheck(pInterface.GetSubscriberInformation(subscriber));
          try
            OleCheck(subscriber.Get_SubscriberID(wStr));
            try
              Memo1.Lines.Add('Subscriber ID: ' + wStr);
            finally
              wStr := '';
            end;

            OleCheck(subscriber.Get_SimIccID(wStr));
            try
              Memo1.Lines.Add('Sim ICC ID: ' + wStr);
            finally
              wStr := '';
            end;

            OleCheck(subscriber.Get_TelephoneNumbers(pPhoneNumberArray));
            try
              OleCheck(SafeArrayGetLBound(pPhoneNumberArray, 1, lPhoneNumLower));
              OleCheck(SafeArrayGetUBound(pPhoneNumberArray, 1, lPhoneNumUpper));
              for J = lPhoneNumLower to lPhoneNumUpper do
              begin
                OleCheck(SafeArrayGetElement(pPhoneNumberArray, J, wStr));
                try
                  Memo1.Lines.Add('Phone #:' + wStr);
                finally
                  wStr := '';
                end;
              end;
            finally
              SafeArrayDestroy(pPhoneNumberArray);
            end;
          finally
            subscriber := nil;
          end;

          // and so on...

          Memo1.Lines.Add('');
        finally
          pInterface := nil;
        end;
      end;
    finally
      SafeArrayDestroy(pInterfaceArray);
    end;
  except
  end;

  Memo1.Lines.Add('process complete');
end;

end.

为什么要使用WShell COM对象调用
cmd.exe
?改用
CreateProcess()
。错误消息表示
netsh
本身无法识别
mbn show interface
命令。你在运行什么系统?你为什么要使用
netsh
?Windows有、使用或获取MBN接口列表。谢谢Remy。你能给我举个例子吗?我只能在MSDN上找到。我在Windows 10 Pro上,没有看到与此或任何相关的类型库。MBN接口的类型库是
mbnapi.tlb
。我在DelphiPraxis论坛上找到了一个(
MbnApi.pas
)。不过,您不需要类型库来调用
Wwan…()
函数。为什么要使用WShell COM对象来调用
cmd.exe
?改用
CreateProcess()
。错误消息表示
netsh
本身无法识别
mbn show interface
命令。你在运行什么系统?你为什么要使用
netsh
?Windows有、使用或获取MBN接口列表。谢谢Remy。你能给我举个例子吗?我只能在MSDN上找到。我在Windows 10 Pro上,没有看到与此或任何相关的类型库。MBN接口的类型库是
mbnapi.tlb
。我在DelphiPraxis论坛上找到了一个(
MbnApi.pas
)。您不需要类型库来调用
Wwan…()
函数。Remy,我尝试使用WwApi方法,但无法识别电话号码或设备ID。这就是我的客户想要的。我有一个批处理文件,它的下面两行是netsh mbn show interfaces>“imei.txt”netsh mbn show readyinfo=“Mobile Broadband Connection”>pno.txt“这正是我想要的,但当我从德尔福打电话给它时就不行了。我使用过ShellExe和CreateProcess,有和没有Wow64DisableWow64FsRedirection,但总是在同一个地方结束。它似乎运行NetSh,但不识别行的其余部分。我只希望数据
imbinterface
在其
ReadyState
SubscriberInformation
属性中提供您要查找的信息。如果坚持使用netsh,则不能使用
操作符对
ShellExecute()
/
CreateProcess()
进行文件重定向。您必须使用管道重定向,如我链接到的MSDN文章所示。完全不要使用临时文件。好的,我又开始使用WwApi了,但是我找不到读取系统状态或订阅信息的方法。我像球一样迷失在杂草丛中。我所要做的就是给我的客户提供航空卡电话号码和设备ID。每条路线似乎都是死胡同。请再次阅读我之前的评论。我没有说WwApi,我说。您是否阅读了我链接到的
imbinterface
文档
imbinterface
提供各种信息。我已经更新了我的示例。我无法复制mbnApi包装。文本已损坏,大约向下三分之二。它不会让我下载链接,即使我是一个注册用户。我在我的任何机器上都找不到mbnapi.tlb,因此我自己无法生成mbnapi包装。我在哪里可以找到它