从Delphi程序发出Netsh命令

时间:2016-07-14 19:07:30

标签: delphi delphi-xe netsh

我正在尝试捕获AirCard的设备ID。我正在使用以下代码,意图将结果存储在我存储在Temp文件夹中的文本文件(imei.txt)中,并遍历内容,查找DEVICE 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.

1 个答案:

答案 0 :(得分:2)

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

Creating a Child Process with Redirected Input and Output

有大量的例子可以在Delphi中展示这种技术。

话虽如此,更好的选择是根本不使用netsh。 Windows 7及更高版本的版本为Mobile Broadband API。您可以直接在代码中枚举MBN接口。

例如,使用WwanEnumerateInterfaces()函数:

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.

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
  WwApi;

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

procedure TfrmMain.GetAirCardInformation;
var
  dwNegotiatedVersion: DWORD;
  hClientHandle: THandle;
  pInterfaceList: PWWAN_INTERFACE_INFO_LIST;
  pInterface: PWWAN_INTERFACE_INFO;
  I: DWORD;
begin
  IMEI := '';
  Memo1.Clear;
  try
    // The value of the first parameter is undocumented!
    // WlanOpenHandle() has a similar parameter, where 1
    // is for XP and 2 is for Vista+. Maybe it is the same
    // for WwanOpenHandle()?...
    //
    if WwanOpenHandle(2, nil, dwNegotiatedVersion, hClientHandle) = 0 then
    try
      if WwanEnumerateInterfaces(hClientHandle, nil, pInterfaceList) = 0 then
      try
        Memo1.Lines.Add('IMEIStrings Count: ' + IntToStr(pInterfaceList.dwNumberOfItems));
        if pInterfaceList.dwNumberOfItems > 0 then
        begin
          pInterface := @pInterfaceList.pInterfaceInfo[0];
          for I := 0 to pInterfaceList.dwNumberOfItems-1 do
          begin
            // use pInterface as needed...

            Memo1.Lines.Add('Desc:' + StrPas(pInterface.strInterfaceDescription));
            Memo1.Lines.Add('Intf:' + GUIDToString(pInterface.InterfaceGuid));

            // and so on ...

            Memo1.Lines.Add('');
            Inc(pInterface);
          end;
        end;
      finally
        WwanFreeMemory(pInterfaceList);
      end;
    finally
      WwanCloseHandle(hClientHandle, nil);
    end;
  except
  end;

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

end.

或者,使用IMbnInterfaceManagerIMbnInterface 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.