Hallo,
ich entwickle i ein Makro innerhalb einer Anwendung.
Die Einschränkungen sind, dass ich mich innerhalb einer Funktion bewege und nur auf vorhandene DLL zugreifen kann.
Ich kann zB nicht auf den DirectoryService zugreifen!
Gibt es eine Möglichkeit , wie in VB auch, über ADO das LDAP abzufragen?
LG
Byte
Hallo Bytechanger,
innerhalb einer Funktion bewege und nur auf vorhandene DLL zugreifen kann.
Ich kann zB nicht auf den DirectoryService zugreifen!
Kannst du weitere Infos bereitstellen ob es eine .NET (Core) od. .NET Framework Umgebung ist?
Via der ADSI OLE DB könnte das in .NET gehen (siehe z.B. ADSI Scripting for Administering Windows Active Directory Networks, aber da ist vermtutlich auch eine weitere Referenz (DLL) nötig.
Frag im Zweifel beim Projektverantwortlichen nach ob nicht doch DirectoryService verwendet werden kann. Das führt schließlich schneller zum Ziel (nämlich die LDAP Abfragen).
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Hallo,
leider gibt es keine Möglichkeit weitere DLL einzubinden. Ich habe da auch keinen Ansprechpartner und bin da etwas auf mich gestellt. Zu allem Überfluss werden Fehler im Makro nur mit einer allgemeinen Fehlermeldung quittiert, so dass immer nur try und error gearbeitet werden kann.
In den Beispielen wird, wie ich bisher auch immer in VB genutzt habe, mit GetObject("LDAP://RootDSE") und CreateObject("ADODB.Command") gearbeitet.
Kann ich diese Verfahrensweise auf C# adaptieren?
also stark vereinfacht auf diese Weise...
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open ...
??
Greets
Byte
Um mit .NET auf LDAP zuzugreifen, wird ADODB
benötigt: ADODB Connection in .NET Application Using C# (also auch eine externe Referenz - und ich bezweifle, daß diese standardmäßig bei deiner Anwendung dabei ist).
Der Code dazu sieht dann so aus: How do I query Active Directory using C# and ADODB?
Edit:
Weißt du denn, welche Provider/Datenbankzugriffsklassen dir zu Verfügung stehen?
Leider nein. Wie gesagt, try and error.
Ich muss auch über den gesamten Namespace zugreifen, da ein "using" innerhalb einer Funktion nicht möglich ist.
Auf der selben Maschine funktioniert ein Zugriff über VBA über
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
Das gibt es in C# nicht, oder gibt es ein Äquivalent?
Das Äquivalent zu CreateObject
in .NET ist Activator.CreateInstance
(zusammen mit Type.GetTypeFromProgID
), s.a. Equivalent code of CreateObject in C#.
Dies wird dann auch von der ADODB
-Klasse intern benutzt und entsprechend typsicher zur Verfügung gestellt.
Mit dem Schlüsselwort dynamic
kannst du dann auf COM-Objekte zugreifen (auch wenn die Typen und Methoden nicht per Referenz eingebunden sind).
Ob das jedoch alles innerhalb einer C#-Methode funktioniert, testest du am besten an einer eigenen (Konsolen-)Anwendung aus - und kopierst anschließend den gesamten Code der Methode.
Ok, ich hatte mich schon mal versucht:
System.Type oConnection = System.Type.GetTypeFromProgID("ADODB.Connection");
System.Type oCommand = System.Type.GetTypeFromProgID("ADODB.Command");
object oConnectionInst = System.Activator.CreateInstance(oConnection);
object oCommandInst = System.Activator.CreateInstance(oCommand);
oConnection.InvokeMember("Open", System.Reflection.BindingFlags.SetProperty, null, oConnectionInst, new object\[1\] {"Provider=ADsDSOObject;"});
object[] args= new object[] { oConnection };
Allerdings stoße ich schon recht früh auf einen Fehler, da diese Funktion dann schon einen Fehler wirft:
oCommand.InvokeMember("ActiveConnection", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.SetProperty, null, oCommandInst, args);
Bitte Code-Tags verwenden, dann ist leichter zu lesen.
Verwende auch du die C#-Tags bei deinen Beiträgen (kannst es auch nachträglich editieren):
oCommand.InvokeMember("ActiveConnection", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.SetProperty, null, oCommandInst, args);
Welchen genauen Fehler gibt es denn, wenn du es bei einem Testprojekt verwendest?
Hallo,
ich bin ein großes Stück weiter gekommen. Ich stehe aktuell nur noch vor 2 Problemen.
Im Ergebnis RecordSet sind die Daten im rs.Fields Feld. Wie kann ich darauf zugreifen?
Mit InvokeMember bekomme ich das Feld an sich, komme aber nicht auf die einzelnen Einträge im Feld???
Ich muss noch die Page Size in Properties setzen. Bisher kann ich über InvokeMember nur einzelne Properties setzen. Wie ändere ich im Object das Property "Page Size" ??
Ich könnte mir das aktuelle Feld Properties ziehen und dann den Eintrag Page Size anpassen und das Feld wieder zurückschreiben, was mich aber zu Problem 1 führt, wie ich auf einzelne Feldwerte zugreife?
Wäre für Hilfe sehr dankbar.
LG
System.Type oConnection = System.Type.GetTypeFromProgID("ADODB.Connection");
System.Type oCommand = System.Type.GetTypeFromProgID("ADODB.Command");
System.Type oRS = System.Type.GetTypeFromProgID("ADODB.Recordset");
object oConnectionInst = System.Activator.CreateInstance(oConnection);
object oCommandInst = System.Activator.CreateInstance(oCommand);
oConnection.InvokeMember("Open", System.Reflection.BindingFlags.SetProperty, null, oConnectionInst, new object[1] {"Provider=ADsDSOObject;"});
object[] args= new object[] { oConnectionInst };
try { oCommand.InvokeMember("ActiveConnection", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.SetProperty, null, oCommandInst, args); }
catch (Exception ex) { System.Windows.Forms.MessageBox.Show("1"+ex.Message); }
string cmdText="SELECT cn,extensionAttribute3 FROM 'LDAP://test.de' WHERE objectClass='user' and objectCategory='person' and samaccountname = '12345678'";
try {oCommand.InvokeMember("CommandText", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.SetProperty, null, oCommandInst, new object[1]{cmdText} );}
catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message); }
/*
//.Properties["Page Size"]=100; <<-- Wie setzt man das??
try {oCommand.InvokeMember("Properties", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.SetProperty, null, oCommandInst, new object[1]{"100"} );}
catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message); }
*/
object objRS = oCommand.InvokeMember("Execute", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.SetProperty, null, oCommandInst, null );
if((object)objRS==null) { System.Windows.Forms.MessageBox.Show("keine Treffer"); return; }
object rEOF=oRS.InvokeMember("EOF", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty, null, objRS, null );
object rBOF=oRS.InvokeMember("bof", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty, null, objRS, null );
bool bEOF=(bool) rEOF;
bool bBOF=(bool) rBOF;
while(!bEOF && !bBOF)
{
System.Windows.Forms.MessageBox.Show("1");
object oo=oRS.InvokeMember("Fields", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty, null, objRS, null );
//------ Benötige Fields["cn"] -----------------------------------------
//--- nächster Eintrag ----------
oRS.InvokeMember("movenext", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.SetProperty, null, objRS, null );
rEOF=oRS.InvokeMember("EOF", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty, null, objRS, null );
rBOF=oRS.InvokeMember("bof", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty, null, objRS, null );
bEOF=(bool) rEOF;
bBOF=(bool) rBOF;
}
oConnection.InvokeMember("Close", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.SetProperty, null, oConnectionInst, null );
Bei Type.InvokeMember kannst du beim Parameter args
(d.h. bei deiner gewählten Überladung der letzte Parameter) ein Array mit dem zu setzenden Wert angeben, also z.B. new Object[] { 100 }
.
Wie man jedoch dort auf eine Index-Eigenschaft zugreift, weiß ich nicht. Evtl. müßtest du dann stattdessen Type.GetProperty verwenden und dort dann beim Parameter types
den Indexdatentyp angeben und anschließend für den Rückgabewert PropertyInfo.SetValue aufrufen.
Edit:
Es geht anscheinend doch mit InvokeMember
. Unter "Hinweise" steht:
Sie können auch verwenden
Type.InvokeMember
, um eine Position in einem Array festzulegen, indem Sie den Index des Werts und dann den nächsten Wert mithilfe von Code wie dem folgenden angeben:
typeof(C).InvokeMember("F", BindingFlags.SetField, null, c, new Object[] {1, "b"});
Dadurch wird die Zeichenfolge "z" im Array, das F enthält, in die Zeichenfolge "b" geändert.
(Ich lieben automatisch Translationen ;- )
PS: Anstatt die BindingFlags
immer wieder einzeln anzugeben, solltest du einfach eine lokale Variable dafür erstellen.
Danke,
das könnte ich zum Setzen des .Properties["Page Size"]
nutzen, allerdings müsste ich dann die Position wissen in Properties, korrekt?
oCommand.InvokeMember("Properties", System.Reflection.BindingFlags.SetField, null, oCommandInst, new object[]{ "Page Size", 100});
Führt zu einem Fehler:
Für ein COM-Objekt muss die eigenschaft "Set", "Get" oder ein Methodenaufruf angegeben werden. Parametername: bindingFlags.
Mein größtes aktuelles Problem ist, dass ich auf das Ergebnis meiner Abfrage nicht komme.
Ich kann zwar das Recordset ansprechen und bekomme auch das Fields Objekt, kann aber nicht auf die Inhalte zugreifen oder weis nicht, wie ich das machen soll.
Der Aufbau der Struktur ist ja:
ErgebnisRecordSet.Fields.Items[0].Value
oder
ErgebnisRecordSet.Fields["name der spalte"].value
Irgendwie muss ich ja an die Daten herankommen. Da stehe ich aktuell auf dem Schlauch und benötige Hilfe.
LG
Der Fehler ist doch klar: Properties
ist eine Eigenschaft, also mußt du BindingFlags.SetProperty
verwenden (sowie die anderen erforderlichen BindingFlags
).
Und bei ErgebnisRecordSet.Fields["name der spalte"].Value
mußt du halt sequentiell vorgehen, also erst ErgebnisRecordSet.Fields["name der spalte"]
und dann für das Objekt auf Value
zugreifen.
Ich kann zwar das Recordset ansprechen und bekomme auch das Fields Objekt, kann aber nicht auf die Inhalte zugreifen oder weis nicht, wie ich das machen soll.
Dann bist du doch auf dem richtigen Weg. Um den Type
für das Fields
Objekt zu bestimmen, einfach field.GetType()
(wenn die Variable field
heißt) benutzen und damit dann InvokeMember
(oder aber GetProperty
, SetProperty
, ...) aufrufen.
Schau dir ansonsten die Beispiele dazu in der Doku an.
Super Danke,
das hatte ich auch schon probiert:
object oo=oRS.InvokeMember("Fields", System.Reflection.BindingFlags.GetProperty, null, objRS, null );
System.Type oFlds = oo.GetType();
object fd=oFlds.InvokeMember("samaccountname", System.Reflection.BindingFlags.GetProperty, null, oo, null );
Allerdings kommt beim .InvokeMember("samaccountname" der Fehler "Unbekannter Name", so dass ich davon ausgehe, dass das oo.GetType ggf nicht richtig funktioniert?!
Also mache ich hier irgendwas falsch?!
LG
Ich hatte dir doch oben in meinem "Edit" den Code aus der Doku gezeigt, wie man eine Index-Eigenschaft bei InvokeMember
benutzt (also als Array bei args
angeben). Bei einem Lesezugriff also nur ein einelementiges Array mit dem Index angeben.
Entschuldige,
leider habe ich es nicht verstanden oder wir reden aneinander vorbei.
Ich möchte die Ergebnisse des RecordSet einlesen.
typeof(C).InvokeMember("F", BindingFlags.SetField, null, c, new Object[] {1, "b"});
funktioniert nicht, da SetField und GetField offenbar nicht bei COM funktionieren (Fehlermeldung: Für ein COM-Objekt muss die eigenschaft "Set", "Get" oder ein Methodenaufruf angegeben werden. Parametername: bindingFlags.).
Ich stehe gerade hier:
object objRS = oCommand.InvokeMember("Execute", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.SetProperty, null, oCommandInst, null );
if((object)objRS==null) { System.Windows.Forms.MessageBox.Show("keine Treffer"); return; }
objRS sollte das RecordSet mit dem Ergebnis beinhalten, dass sich in vba über objRS .Fields("cn").value oder objRS ("cn").value abrufen lassen sollte, wobei "cn" der Spaltenname ist (SELECT cn,extensionAttribute3 FROM 'LDAP://test.de' WHERE ... ).
Alle meine Versuche auf die Spalten im objRS zurückzugreifen schlugen nun fehl. Da wäre ich für Hilfe sehr dankbar!!
OK, ich versuche es mal direkt hier im Editor (da ich jetzt kein Projekt dazu erzeugen möchte):
var bfGetProperty = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty;
// Code for objRS.Fields["samaccountname"]
object field = oRS.InvokeMember("Fields", bfGetProperty, null, objRS, new Object[] { "samaccountname" } );
// Code for field.Value
object value = field.GetType().InvokeMember("Value", bfGetProperty, null, field, null);
Und zu dem Fehler bzgl. BindingFlags.SetField
schreibe ich jetzt nichts weiteres...
Jepp,
vielen Dank, das war die Hilfe, die mir fehlte!
Super, das funktioniert!! D A N K E!
Jetzt zu meinem zweiten Problem, Properties für "Page Size".
Das hier gibt keine Fehlermeldung.
Ist das so korrekt umgesetzt?
oCommand.InvokeMember("Properties", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.SetProperty, null, oCommandInst, new object[]{"Page Size", 100} );
Ja, so sollte es funktionieren.
Kannst es ja danach mit dem analogen Getter-Zugriff überprüfen:
int pageSize = (int)oCommand.InvokeMember("Properties", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetProperty, null, oCommandInst, new object[]{"Page Size"} ); // bzw. die Variable bfGetProperty verwenden ;-)