Netzwerk Umgebung durchsuchen

toma3

Grünschnabel
Hallo,
ich versuche eine GUI zu programmieren zum folgenden Beitrag:
http://www.tutorials.de/forum/c-c-tutorials/117448-c-windows-netzwerk-durchsuchen-teil-1-a.html

Das Programm als Konsolenanwedung klappt wunderbar, jedoch mit GUI komm ich ins stottern. Wahrscheinlich zu wenig Hintergrundwissen....

Meine Vorgehensweise:

1. Erstelle eine neue VCL -Formularanwendung (Borland C++ Builder) (Oberfläche mit einem Button als Auslöser, ComboBoxEx zur Auswahl der Domänen und CheckListBox für die einzelnen Rechner, die An- oder Abgewählt werden können, zur Weiterverarbeitung gedacht)
2. Binde Dateien (netscan.cpp und netscan.h) im Projekt ein
netscan.h
Code:
#ifndef NETSCAN_H
#define NETSCAN_H

//#error  !!  Bitte das Projekt gegen die mpr.lib linken ==> dann diese Zeile löschen

#include <windows.h>
#include <string>
#include <map>
#include <winerror.h>

using namespace std;

typedef map<string,string> StringToString;
typedef void (*func)(void *, DWORD, string);

class NetworkBase
{
public:

	
	bool NetScan(StringToString* dic,
							 void* obj,
							 func eh,
							 string resource="",
							 DWORD displaytype = RESOURCEDISPLAYTYPE_GENERIC,
							 DWORD type = RESOURCETYPE_ANY, 
							 LPNETRESOURCE lpnr=NULL);

};
nescan.cpp
Code:
#include "netscan.h"

bool NetworkBase::NetScan(StringToString* dic,
						 void* obj,
						 func eh, 
						 string resource,
						 DWORD displaytype, 
						 DWORD type,
						 LPNETRESOURCE lpnr)
{
  DWORD dwResult, dwResultEnum;
  HANDLE hEnum;
  DWORD cbBuffer = 16384;      
  DWORD cEntries = -1;         
  LPNETRESOURCE lpnrLocal;    
  DWORD i;


	// wenn wir eine Resource übergeben, dann erstellen wir und eine
	// NETRECOURCE Struktur
	if(lpnr == NULL && !resource.empty())
	{
		NETRESOURCE rec;
		rec.dwScope				= RESOURCE_GLOBALNET;
		rec.dwType				= type;
		rec.dwDisplayType = displaytype;
		
		// wenn wir nach einer Freigabe oder nach einem Netzwerkdrucker suchen
		if( (type == RESOURCETYPE_DISK) || (type == RESOURCETYPE_PRINT) )
			rec.dwUsage = RESOURCEUSAGE_CONNECTABLE;
		else
			rec.dwUsage				= RESOURCEUSAGE_CONTAINER;
		
		rec.lpLocalName		= NULL;
		rec.lpComment			= NULL;
		rec.lpProvider		=	NULL;
		rec.lpRemoteName	= (char *)resource.c_str();
		lpnr = &rec;	
	}

	
	// open the enumeration of network
	dwResult = WNetOpenEnum(RESOURCE_GLOBALNET, // alle Netzwerkressourcen
													type,		// alle Ressources
													0,									// enumerate alle Ressources	
													lpnr,								// die vordefinierte Ressource z.B. ein Server zum scannen seiner Shares
													&hEnum );						// handle zu der Resource
	
	if(dwResult != NO_ERROR)
	{
		eh(obj, dwResult, "");
		return false; // Error Handling
	}
		

	
	// start enumeration
	// Schleife bis the enumeration keine Items mehr findet
	do
	{
		// Speicher für das struct-array allokieren
		lpnrLocal = new NETRESOURCE[cbBuffer];
	
		// es konnte kein Speicher allokiert werden
		if(!lpnrLocal)
		{
			eh(obj, 0, "Speicher allokieren fehlgeschlagen");
			return false;
		}
			
		
		// denn Speicher auf 0 setzen
		ZeroMemory(lpnrLocal, cbBuffer);
		

		// Das Netzwerk enumieren und die Ressourcen raus holen
		dwResultEnum = WNetEnumResource(hEnum,      // resource handle
                                    &cEntries,  // auf -1 vorbelegt
                                    lpnrLocal,  // Zeiger auf die lokale Struktur
                                    &cbBuffer); // Puffergröße
		
		// wenn kein Fehler auf getreten ist ( es wurden Ressourcen gefunden
		if (dwResultEnum == NO_ERROR)
    {
      // Die Ressourcen iterieren, die in dieser Ebene gefunden wurden
			for(i = 0; i < cEntries; i++)
      {
				// hier holen wir die Daten raus
				string name = ""; 
				string comment = ""; 
				
				if(lpnrLocal[i].lpRemoteName)
					name= (char*)lpnrLocal[i].lpRemoteName;
				
				if(lpnrLocal[i].lpComment)
					comment= (char*)lpnrLocal[i].lpComment;

				// Überprüfen, ob die Resource ein Container ist
				if(RESOURCEUSAGE_CONTAINER == (lpnrLocal[i].dwUsage & RESOURCEUSAGE_CONTAINER))
				{
					

					// jetzt überprüfen wir ob die Ressource der angegebenen Ebene von <displaytype> entspricht
					if(displaytype == lpnrLocal[i].dwDisplayType)
					{
						// wir ersetzten alle Backslashes durch nichts fals vorhanden
						while(name.find("\\") != string::npos)
							name.replace(name.find("\\"), strlen("\\"), "");

						//jetzt fügen wir die gefundene Resource in unsere map ein
						dic->insert(StringToString::value_type(name, comment));
					}
					// wenn die Resource nicht userer eben entspricht, aber trotzdem ein Conteiner ist (s.o.)
					// dann rufen wir die NetScan-Funktion rekursiv auf
					else
					{
						if(!NetScan(dic, obj, eh, name, displaytype, type, &lpnrLocal[i] ))
						{
							eh(obj, 0, "NetScan fehlgeschlagen");
						}						
					}
				}
				else
				{
				
					// Wenn die Resource kein Container ist (evtl. eine Datenfreigabe oder Drucker
					// dann überprüfen wir trotzdem, ob sie der angegebenen Ebene enspricht
					// denn dann können wir sie in die map einfügen
					if(displaytype == lpnrLocal[i].dwDisplayType)
					{
						// wir entfernen zunächst den Rechnernamen
						if(name.find(resource) != string::npos)
							name.replace(name.find(resource), resource.size(), "");
						
						//dann entfernen wir wieder alle Backslashes
						while(name.find("\\") != string::npos)
								name.replace(name.find("\\"), strlen("\\"), "");

						// Schließlich können wir die Resource in die map einfügen
						dic->insert(StringToString::value_type(name, comment));
					}
				}
			}
		}
		else if (dwResultEnum != ERROR_NO_MORE_ITEMS)
		{	
			// löschen des arrays
			delete[] lpnrLocal;
			eh(obj, dwResultEnum, "");
			// ein Fehler ist in dieser Ebene aufgetreten wir brechen die Schleife ab
			break;
		}

		// löschen des arrays
		delete[] lpnrLocal;
	}
	while(dwResultEnum != ERROR_NO_MORE_ITEMS);		// ende der Schleife

	

	// schließen der Enumeration
	dwResult = WNetCloseEnum(hEnum);
  
	
  if(dwResult != NO_ERROR)
	{
		eh(obj, dwResult, "");
		return false; 
	}
	

	return true;
}
3.main.cpp versuche ich in meine Unit1.cpp zu integrieren:
Unit1.cpp
Code:
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "netscan.cpp"
#include "netscan.h"
#include <iostream>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void ErrorHandling(void*, DWORD error, string errortext);


void __fastcall TForm1::Button1Click(TObject *Sender)
{
	StringToString dic;
	// der Iterator der für die Map
	StringToString::iterator iter;
	bool ret;
	NetworkBase net;
	
/*
 *				Domain suchen
 */
	// hier holen wir uns die domains/workgroups
	ret = net.NetScan(&dic, NULL, &ErrorHandling, "", RESOURCEDISPLAYTYPE_DOMAIN);

	if(!ret)
	{
		ShowMessage((NULL, 0, "NetScan fehlgeschlagen"));
		exit;
		//return -1;
	}

	// wurden domains gefunden ?
	if(!dic.size())
	{
		ShowMessage("Keine Domains/Wourkgroups gefunden");
		exit;
		//return -1;
	}

	// iterate der domains und ausgeben
	iter = dic.begin();
	//cout << "Folgende Domains gefunden:" << endl;
	while(iter != dic.end())
	{

		ShowMessage(iter->first);
		cout << "Domain: " << iter->first << " | Kommentar: " << iter->second << endl;
		iter++;
	}

	// wir speichern uns die 1. domain
	iter = dic.begin();
	string firstDomain = iter->first;
	// dann löschen wir die map
	ComboBoxEx1->Items->Text=iter->first;
	dic.clear();

/*
 *				Rechner in der 1. Domain suchen
*/


	// nun holen wir uns die Rechner aus der 1. Domain
	cout << "\n\nRechner aus " << firstDomain << ":" << endl;

	// aufruf der NetScan-Funktion mit der 1. Domain
	ret = net.NetScan(&dic, NULL, &ErrorHandling, firstDomain, RESOURCEDISPLAYTYPE_SERVER);
	if(!ret)
	{
		ErrorHandling(0, NULL, "NetScan fehlgeschlagen");
		exit;
		//return -1;
	}

	// wurden Rechner gefunden
	if(!dic.size())
	{
		cout << "Keine Rechner in " << firstDomain << " gefunden" << endl;
		exit;
		//return -1;
	}
	// wenn ja dann ausgeben
	iter = dic.begin();
	cout	<< "\nFolgende Rechner in " << firstDomain << " gefunden:" << endl;
	while(iter != dic.end())
	{
		cout << "Rechner: " << iter->first << " | Kommentar: " << iter->second << endl;
		iter++;
	}

	// wir speichern uns den 1. Rechner
	iter = dic.begin();
	string firstServer = iter->first;
	// dann löschen wir die map
	dic.clear();

}

void ErrorHandling(void*, DWORD error, string errortext)
{
	cout << " FEHLER ";
	if(error)
		cout << "-> Fehlernummer: " << error << endl;
	if(errortext.empty())
		cout << "Hier koennte jetzt noch eine Beschreibung des Fehlers stehen\n" << endl;
	else
		cout << "-> " << errortext << endl;
}

Erste rot markierte Zeile ist der Auslöser und dann folgen meine Änderungen in dem ich versuche die Ausgabe (statt cout)in ShowMessage oder ComboBoxEx zuumwandeln:
ComboBoxEx1->Items->Text=iter->first;
So und beim Compilieren kommt schon ein Fehler:
Konvertierung von 'const string' nach 'UnicodeString' nicht möglich

So wie kann ich das am besten lösen? Ist meine Vorgehensweise falsch?
 
Zuletzt bearbeitet:
Hi,

die Frage ist was ist 'UnicodeString'? in meinem Tutorial hab ich mich nicht wirklich um encoding geschert und hab die ANSI-Api verwendet.
Borland VCL (hab ich keine Ahnung davon) erwartet warscheinlich bei
ShowMessage()
bzw.
ComboBoxEx1->Items->Text =
einen 'UnicodeString' das wird warscheinlich ein Datentyp sein, der Strings in beliebigen Encodings verwalten kann.
Also musst du dafür sorgen, dass du den std::string den dir der Iterator zurückliefert in einen UnicodeString konvertierst.

Gruß Daniel
 
Hey,
also ich hab hier mal ein paar Links für ein API-Tutorial. Es ist wirklich gut, um dein 'Hintergrundwissen' mal ein bisschen aufzubessern :-)
http://www.win-api.de/tutorials.php

Du schreibst in deinem Code zudem 'cout', warum? cout ist doch nur für Konsolenanwendungen gedacht, meiner Meinung nach. Bei ner Win-API oder auch GUI gibt es kein 'cout' um etwas anzuzeigen. Außer du willst es in einer Konsole anzeigen.

Zu deinem Unicode: Ich bin kein Unicode-Experte, aber Unicode
spezifiziert primär die Interpretation von Werten als Zeichen. Dieser Link wird dir vielleicht helfen: http://de.wikipedia.org/wiki/UTF
 
Der ursprüngliche Quellcode enthält cout usw..(als Konsolenanwedung schon getestet)
Ich versuche mit GUI das ganze anzupassen und ersetze cout mit ShowMessage als Test, jedoch beim Compilieren kommt der Fehler mit dem Unicode...
Und ich weiß es nicht wie ich es wandeln soll.
 
Hab was gefunden, nur mit dem Verstehen tue ich mir schwer...

TCHAR (deklariert in der Header-Datei tchar.h) ist ein typedef (Alias), der entweder zu char oder zu wchar_t abgebildet wird. Wenn Sie portierbaren Quelltext schreiben möchten, sollten Sie TCHAR verwenden, anstatt char oder wchar_t hart zu codieren.

Die Option _TCHAR entspricht steuert die Floating-Definition von _TCHAR. Sie haben folgende Möglichkeiten:

wchar_t: legt UNICODE fest und _UNICODE definiert und verwendet Wide-Definitionen der Standardbibliothek und der API-Funktionen.
char: _TCHAR verwendet keine Wide-Definition.
Die Option _TCHAR entspricht wird auf der Seite ProjektOptionenVerzeichnisse und Bedingungen gesetzt.

Verwendung von _TCHAR entspricht wchar_t für die VCL
Die VCL ist in Unicode implementiert und erwartet immer Unicode. Für die Verwendung der VCL sollten Sie _TCHAR entspricht auf wchar_t setzen. Beispielsweise kann der folgende Code erst compiliert werden, wenn Sie _TCHAR auf wchar_t gesetzt haben:
Code:
TResourceStream* res =  new 
    TResourceStream(HInstance, ResourceId, RT_RCDATA);
Wenn _TCHAR char ist, wird RT_RCDATA einem char* zugeordnet, aber die VCL erwartet wchar_t.

Verwendung der Funktion _TEXT für Konstanten-Strings
Mit der Funktion _TEXT oder _T stellen Sie sicher, dass für Konstanten-Strings ANSI oder Unicode verwendet wird. Zum Beispiel:
Code:
::MessageBox(0, _TEXT("The message"), 
    _TEXT("The caption"), MB_OK);
Codeänderungen sind für _TCHAR entspricht wchar_t erforderlich
Bevor Sie die Option _TCHAR auf wchar_t setzen können, muss Ihr Projekt einen Einsprungspunkt namens _tmain oder _tWinMain haben. Neue (mit C++Builder 2009 erstellte) Projekte enthalten standardmäßig diese Einsprungspunkte, aber bei importierten Projekten müssen Sie sie eventuell manuell hinzufügen. Sie müssen auch die Header-Datei tchar.h einbeziehen, die die Floating-Definitionen und die erforderlichen Einsprungspunkte enthält. Eine Liste der in tchar.h enthaltenen Floating-Funktionen, finden Sie unter Floating-Funktionen.
 
so hab was gefunden und der Fehler mit Unicode kommt nicht mehr:

Code:
Unicode xyz;
xyz.c_str(),iter->first;
ShowMessage(xyz);

bekomme aber leere Message Box...

Vielleicht ist die Umwandlung auch falsch?
 
hab eine andere Routine gefunden und es t

Code:
void __fastcall TForm1::Button2Click(TObject *Sender)
{

  LPSERVER_INFO_101 pBuf = NULL;
  LPSERVER_INFO_101 pTmpBuf;
  DWORD dwLevel = 101;
  DWORD dwPrefMaxLen = -1;
  DWORD dwEntriesRead = 0;
  DWORD dwTotalEntries = 0;
  DWORD dwTotalCount = 0;
  DWORD dwServerType = SV_TYPE_SERVER; // all servers
  DWORD dwResumeHandle = 0;
  NET_API_STATUS nStatus;
  LPTSTR pszServerName = NULL;
  DWORD i;

  // Call the NetServerEnum function to retrieve information
  // for all servers, specifying information level 101.
  nStatus = NetServerEnum(pszServerName,
                          dwLevel,
						  (LPBYTE *) &pBuf,
                          dwPrefMaxLen,
                          &dwEntriesRead,
						  &dwTotalEntries,
                          dwServerType,
                          NULL,
						  &dwResumeHandle);

  // If the call succeeds,
  if ((nStatus == NERR_Success) || (nStatus == ERROR_MORE_DATA))
  {
    if ((pTmpBuf = pBuf) != NULL)
	{
      // Loop through the entries and
      // print the data for all server types.
	  for (i = 0; i < dwEntriesRead; i++)
      {
		//TListItem  *ListItem = ListView1->Items->Add();
		CheckListBox1->Items->Add(pTmpBuf->sv101_name);
		//ListItem->Caption = pTmpBuf->sv101_name;
		//ListItem->SubItems->Add(pTmpBuf->sv101_platform_id);
		//ListItem->SubItems->Add(String(pTmpBuf->sv101_version_major) + "." +
		//                        String(pTmpBuf->sv101_version_minor));
		//ListItem->SubItems->Add(pTmpBuf->sv101_type);

		// Check to see if the server is a domain controller;
		// if so, identify it as a PDC or a BDC.
		/*if (pTmpBuf->sv101_type & SV_TYPE_DOMAIN_CTRL)
		  ListItem->SubItems->Add("PDC");
		else if (pTmpBuf->sv101_type & SV_TYPE_DOMAIN_BAKCTRL)
		  ListItem->SubItems->Add("BDC");

		ListItem->SubItems->Add(pTmpBuf->sv101_comment);
		*/
		pTmpBuf++;
		dwTotalCount++;
	  }

      // Display a warning if all available entries were not enumerated,
	  // print the number actually enumerated, and the total number available.
	  //if (nStatus == ERROR_MORE_DATA)
	  //  Application->MessageBox(("Total entries: " + String(dwTotalEntries)).c_str(),
	  //							 "More entries available!", MB_OK | MB_ICONSTOP);

	  //Caption = "Entries enumerated: " + String(dwTotalCount);
	  StatusBar1->Panels->Items[1]->Text=dwTotalCount;
    }
  }

  // Free the allocated buffer.
  if (pBuf)
	NetApiBufferFree(pBuf);
}

..kann jetzt entspannt ins WE gehen...:p
Danke für alle Antworten
 
Zurück