2016-05-16 3 views
0

Ich schreibe eine App, die von einem Windows-Dienst mit der Windows-API interagiert.Aufruf von EnumServicesStatusEx in Go, Speicherzuordnung?

Nach der großen Hilfe von @chowey here, habe ich irgendwie den Hang der Dinge und begann eine grundlegende Bibliothek, die ich auf GitHub here gesetzt habe.

Ich bin jetzt zu "Services" mit der Anforderung, alle Windows-Dienste auf einer Maschine aufzulisten, starten, stoppen und neu starten. Der Start/Stopp/Neustart sieht ziemlich einfach aus, sobald Sie einen Service-Handle haben, mit dem Sie arbeiten können, aber ich habe Schwierigkeiten, eine Liste der installierten Dienste zu erhalten.

EnumServicesStatusEx in Advapi32.dll ist die Funktion, die ich aufrufen muss, aber es erfordert einen Zeiger auf vorab zugewiesenen Speicher für ein Array von ENUM_SERVICE_STATUS_PROCESS Strukturen.

Sie können die Funktion mit einem Null-Zeiger aufrufen und es wird die erforderliche Speicherzuordnungsgröße zurückgeben, aber ich glaube nicht, dass es eine Möglichkeit gibt, Speicher in Go direkt zuzuweisen.

Zuerst dachte ich, ich könnte die Speicherzuweisung Anforderung bekommen, dividieren Sie es durch die SizeOf der Struktur mit dem unsicheren Paket, erstellen Sie ein Stück mit dieser Anzahl von Elementen, dann übergeben Sie einen Zeiger auf das erste Element der Funktion, aber es besagt, dass der Speicher Platz für die Zeichenfolgedaten enthalten muss, was dies nicht tun würde.

Weiß jemand, wie dies erreicht werden könnte, bitte schön? :).

+0

Von EnumServicesStatusEx doco: 'lpServices [out, optional] Ein Zeiger auf den Puffer, der die Statusinformationen empfängt. ... Um die erforderliche Größe zu bestimmen, geben Sie NULL für diesen Parameter und 0 für den Parameter cbBufSize an. Die Funktion wird fehlschlagen und GetLastError wird ERROR_MORE_DATA zurückgeben. Der Parameter pcbBytesNeeded erhält die erforderliche Größe.' Sobald Sie wissen, wie groß Ihr Puffer sein muss, rufen Sie EnumServicesStatusEx erneut mit einem Puffer dieser Größe auf. – alex

+0

Hallo @alex, wie oben beschrieben, kenne ich keine Möglichkeit, Go Speicher direkt zu reservieren. Sie müssen ihm eine Datenstruktur geben, um Speicher zu reservieren, und diesen Speicher über das unsichere Paket verfügbar machen. – iamacarpet

+1

Sie haben es selbst gesagt: 'Erstellen Sie ein Slice mit dieser Anzahl von Elementen, dann übergeben Sie einen Zeiger auf das erste Element der Funktion '. Von EnumServicesStatusEx doco: 'cbBufSize [in] Die Größe des Puffers, auf den der lpServices-Parameter zeigt, in Bytes. So können Sie ein Array von cbBufSize-Bytes erstellen und die Adresse des ersten Elements dieses Arrays an EnumServicesStatusEx übergeben. Siehe zum Beispiel https://github.com/golang/go/blob/master/src/internal/syscall/windows/registry/value.go#L71. Im Gegensatz zum Beispiel sollten Sie mit der Übergabe von lpServices = nil gemäß EnumServicesStatusEx doco beginnen. – alex

Antwort

0

Nach den Vorschlägen von @alex, habe ich den folgenden Beispielcode funktioniert.

Sieht aus, als ob wir ein Byte-Slice der richtigen Größe erstellen und dann die unsichere Klasse verwenden, um zu unserem Struct-Typ zu casten.

_, _, _ = svcEnumServicesStatusEx.Call(
     uintptr(handle), 
     uintptr(uint32(SVC_SC_ENUM_PROCESS_INFO)), 
     uintptr(uint32(SVC_SERVICE_WIN32)), 
     uintptr(uint32(SVC_SERVICE_STATE_ALL)), 
     uintptr(0), 
     0, 
     uintptr(unsafe.Pointer(&bytesReq)), 
     uintptr(unsafe.Pointer(&numReturned)), 
     uintptr(unsafe.Pointer(&resumeHandle)), 
     uintptr(0), 
    ) 

    if bytesReq > 0 { 
     var buf []byte = make([]byte, bytesReq) 

     ret, _, _ := svcEnumServicesStatusEx.Call(
      uintptr(handle), 
      uintptr(uint32(SVC_SC_ENUM_PROCESS_INFO)), 
      uintptr(uint32(SVC_SERVICE_WIN32)), 
      uintptr(uint32(SVC_SERVICE_STATE_ALL)), 
      uintptr(unsafe.Pointer(&buf[0])), 
      uintptr(bytesReq), 
      uintptr(unsafe.Pointer(&bytesReq)), 
      uintptr(unsafe.Pointer(&numReturned)), 
      uintptr(unsafe.Pointer(&resumeHandle)), 
      uintptr(0), 
     ) 

     if ret > 0 { 
      var sizeTest ENUM_SERVICE_STATUS_PROCESS 
      iter := uintptr(unsafe.Pointer(&buf[0])) 

      for i := uint32(0); i < numReturned; i++ { 
       var data *ENUM_SERVICE_STATUS_PROCESS = (*ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(iter)) 

       fmt.Printf("Service Name: %s - Display Name: %s - %#v\r\n", syscall.UTF16ToString((*[4096]uint16)(unsafe.Pointer(data.lpServiceName))[:]), syscall.UTF16ToString((*[4096]uint16)(unsafe.Pointer(data.lpDisplayName))[:]), data.ServiceStatusProcess) 

       iter = uintptr(unsafe.Pointer(iter + unsafe.Sizeof(sizeTest))) 
      } 
     } else { 
      return nil, fmt.Errorf("Failed to get Service List even with allocated memory.") 
     } 
    } else { 
     return nil, fmt.Errorf("Unable to get size of required memory allocation.") 
    }