2016-07-14 7 views
0

Ich versuche, die Geräte-ID einer AirCard zu erfassen. Ich verwende den folgenden Code mit der Absicht, die Ergebnisse in einer Textdatei (imei.txt) zu speichern, die ich im Ordner "Temp" abspeichere, und den Inhalt durchzusehen, um nach DEVICE ID zu suchen.Ausgabe von Netsh-Befehl von Delphi-Programm

Das Problem ist, dass es nur schreibt "Der folgende Befehl wurde nicht gefunden: mbn show interface." zu der Datei.

Ich habe den Netsh-Befehl von der Befehlszeile getestet und es gibt zurück, was ich erwarten würde.

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

Es ist fehlgeschlagen, den NetSh-Befehl ordnungsgemäß zu verarbeiten. Übergebe ich es korrekt durch die Comspec? Es scheint nicht den "NetSh" Befehl auszuführen und tut so, als ob ich "mbn" von der Eingabeaufforderung aus ausführen würde.

Dank

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

Warum Verwenden Sie das WShell COM-Objekt zum Aufrufen 'cmd.exe'? Verwenden Sie stattdessen 'CreateProcess()'. Die Fehlermeldung bedeutet, dass "netsh" selbst den Befehl "mbn show interface" nicht erkennt. Auf welchem ​​System laufen Sie? Warum benutzt du überhaupt netsh? Windows hat eine [Mobile Broadband API] (https://msdn.microsoft.com/en-us/library/windows/desktop/dd323271.aspx), verwenden Sie ['WwanEnumerateInterfaces()'] (https://msdn.microsoft .com/de-us/library/windows/desktop/dn313190.aspx) oder ['IMbnInterfaceManager'] (https://msdn.microsoft.com/en-us/library/windows/desktop/dd430416.aspx) zu bekommen eine Liste von MBN-Schnittstellen. –

+0

danke Remy. Kannst du mich auf ein Beispiel hinweisen? Alles, was ich finden kann, ist auf MSDN. Ich bin auf Windows 10 Pro und sehe keine Typbibliothek für diese oder irgendetwas Ähnliches. –

+0

Die TypeLibrary für die MBN-Schnittstellen ist 'mbnapi.tlb'. Ich habe eine [Delphi-Einheit dafür] (http://www.delphipraxis.net/189688-windows-mbn-api-fuer-delphi-xe-portieren.html) ('MbnApi.pas') im DelphiPraxis-Forum gefunden. Sie benötigen jedoch keine TypeLibrary, um die 'Wwan ...()' -Funktionen aufzurufen. –

Antwort

2

Sie sollten das WShell COM-Objekt laufen cmd.exe nicht werden. Das ist Overkill. Sie können stattdessen CreateProcess() verwenden. Wenn Sie jedoch cmd.exe programmierbar ausführen, können Sie die Ausgabe nicht mit dem Operator > umleiten, der nur in einem tatsächlichen Befehlsfenster funktioniert. Sie können stattdessen die Struktur STARTUPINFO verwenden, um die Ausgabe an eine anonyme Pipe umzuleiten, die mit CreatePipe() erstellt wurde, und dann können Sie von dieser Pipe mit ReadFile() lesen. Keine Notwendigkeit, eine temporäre Datei zu verwenden. MSDN hat einen Artikel zu diesem Thema:

Creating a Child Process with Redirected Input and Output

Es gibt viele Beispiele im Umlauf, die diese Technik in Delphi demonstrieren.

Das Gesagte, ist eine bessere Option, netsh überhaupt nicht zu verwenden. Windows 7 und höher haben eine Mobile Broadband API. Sie können die MBN-Schnittstellen direkt in Ihrem Code aufzählen.

Zum Beispiel mit der WwanEnumerateInterfaces() Funktion:

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. 

Alternativ können die IMbnInterfaceManager und IMbnInterface COM-Schnittstellen verwenden, die Sie detailliertere Informationen geben:

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, Ich habe versucht, mit dem WwApi Ansatz, aber ich bin nicht in der Lage, die Telefon-Nr oder Geräte-ID herauszukitzeln. Das ist alles, was mein Kunde will. Ich habe eine Batch-Datei, die die 2 Zeilen unter Netsh mbn zeigt Schnittstellen> "imei.txt" Netsh mbn zeigen readyinfo = "Mobile Breitbandverbindung"> pno.txt " Es tut genau das, was ich will, aber nicht wenn ich Rufen Sie es von Delphi.Ich habe sowohl ShellExe und CreateProcess, mit und ohne Wow64DisableWow64FsRedirection aber immer am selben Ort.Es scheint, die NetSh laufen aber nicht den Rest der Zeile.Ich möchte nur die Daten –

+0

'IMbnInterface 'liefert die Informationen, nach denen Sie suchen, in den Eigenschaften 'ReadyState' und' SubscriberInformation'. Wenn Sie darauf bestehen, netsh zu verwenden, können Sie den '>' - Operator nicht für die Dateiumleitung mit 'ShellExecute()'/'CreateProcess () '. Sie müssen die Umleitung von Leitungen verwenden, wie in dem MSDN-Artikel gezeigt, mit dem ich Sie verlinkt habe. Verwenden Sie keine temporäre Datei. –

+0

Okay, ich bin wieder da mit WwApi, aber ich kann nicht zu ReadyState oder SubscriberInformation finden. Ich bin verloren wie ein Ball im hohen Gras. Alles, was ich tun möchte, ist, meinen Kunden die Aircard-Telefonnummer und die Geräte-ID zu besorgen. Jede Route scheint eine Sackgasse zu sein. –