2016-07-14 2 views
0

Я пытаюсь захватить идентификатор устройства AirCard. Я использую следующий код с намерением сохранить результаты в текстовом файле (imei.txt), который я храню в папке Temp, и просматриваю содержимое, ища идентификатор DEVICE.выдача команды Netsh из программы Delphi

Проблемы в том, что он пишет только «Следующая команда не была найдена: 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. 
+0

Почему вы используете COM-объект WShell для вызова 'Cmd.exe'? Вместо этого используйте 'CreateProcess()'. Сообщение об ошибке означает, что 'netsh' сам не распознает команду' mbn show interface'. В какой системе вы работаете? Почему вы используете 'netsh' вообще? В Windows есть [API широкополосной связи] (https://msdn.microsoft.com/en-us/library/windows/desktop/dd323271.aspx), используйте ['WwanEnumerateInterfaces()'] (https://msdn.microsoft .com/en-us/library/windows/desktop/dn313190.aspx) или ['IMbnInterfaceManager'] (https://msdn.microsoft.com/en-us/library/windows/desktop/dd430416.aspx), чтобы получить список интерфейсов MBN. –

+0

спасибо Реми. Можете ли вы указать мне пример своего рода? Все, что я могу найти, находится на MSDN. Я нахожусь в Windows 10 Pro и не вижу библиотеки типов для этого или чего-то связанного. –

+0

ТипLibrary для интерфейсов MBN - 'mbnapi.tlb'. Я нашел для него блок Delphi (http://www.delphipraxis.net/189688-windows-mbn-api-fuer-delphi-xe-portieren.html) ('MbnApi.pas') на форуме DelphiPraxis. Однако вам не нужна TypeLibrary для вызова функций 'Wwan ...()'. –

ответ

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. 

В качестве альтернативы, используя IMbnInterfaceManager и IMbnInterface 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. 
+0

Remy, Я попытался использовать подход WwApi, но я не могу дразнить телефон или идентификатор устройства. Это все, что хочет мой клиент. У меня есть командный файл с двумя строками ниже netsh mbn show interfaces> "imei.txt" netsh mbn show readyinfo = "Мобильная широкополосная связь"> pno.txt " Он делает то, что я хочу, но не тогда, когда я назовите его из Delphi.Я использовал и ShellExe, и CreateProcess, с Wow64DisableWow64FsRedirection и без него, но всегда в одном месте. Кажется, он запускает NetSh, но не распознает остальную часть строки. Мне просто нужны данные –

+0

'IMbnInterface 'предоставляет информацию, которую вы ищете, в ее свойствах ReadyState и' SubscriberInformation'. Если вы настаиваете на использовании netsh, вы не можете использовать оператор '>' для перенаправления файлов с помощью команды ShellExecute() '/' CreateProcess()) '. Вы должны использовать перенаправление каналов, как показано в статье MSDN, к которой я привязал вас. Не используйте временный файл вообще. –

+0

Хорошо, я вернулся к используя WwApi, но я не могу найти свой путь к ReadyState или SubscriberInformation. Я потерялся, как мяч в высоких сорняках. Все, что я хочу сделать, это получить моему клиенту номер телефона с номером телефона и идентификатором устройства. Каждый маршрут кажется тупиковым. –