Laden...

[gelöst] Sage Office Line: Microsoft.Office.Interop.Access._CurrentProject gefordert/Was übergeben?

Erstellt von Christoph Burgdorf vor 14 Jahren Letzter Beitrag vor 14 Jahren 4.973 Views
Christoph Burgdorf Themenstarter:in
365 Beiträge seit 2004
vor 14 Jahren
[gelöst] Sage Office Line: Microsoft.Office.Interop.Access._CurrentProject gefordert/Was übergeben?

Hallo miteinander,

wie bereits in diesem Thread beschrieben, versuche ich gerade ein Access Programm, mit dem wir die Sage Office Line fernsteuern auf .NET zu portieren. Hierzu habe ich alle nötigen COM DLLs referenziert und Visual Studio hat mir hübsche Wrapper Assemblies dafür erstellt 😃

Nun ist es aber so, dass man die Methoden jetzt teilweise etwas anders benutzen muss als unter Access.

Unter Access habe ich die Verbindung wie folgt hergestellt:


Public Function bConnectDbOL(ByVal sDataSource As String, _
                             ByVal sBenutzer As String, _
                             ByVal iMandant As Integer, Optional _
                             ByVal sPassword As String = "" _
                             ) As Boolean
                             
  '// Setzen der OfficeLine Engine
  Set goEngine = New OLEngine
  
  '// Anmelden an der Office Line Engine / Datenbank
  Set goDs = goEngine.oServer.oPublicDataSources.oItem(sDataSource)
  Set goMandant = OLEngine.oCreateMandant(goDs, "Abf", iMandant, sBenutzer, sPassword)
End Function

Unter .NET sieht das jetzt bisher so aus:


using Access = Microsoft.Office.Interop.Access;
[..]

OLEngine engine = new OLEngineClass();
OLServer server =	engine.oServer();
Object sourceName = "myDataSourceName";
OLDataSource dataSource = server.oPublicDataSources.oItem(ref sourceName);
        
string user = "myUser";
string pw = "myPassword";
string appName = "Abf";
short manId = 4;

int hWnd = 0; //wird jetzt von oCreateMandant() benötigt
OLSecurity security = new OLSecurityClass(); //wird jetzt von oCreateMandant() benötigt       	
Access.Application application = new Access.ApplicationClass();        	
Access._CurrentProject project = application.CurrentProject; //wird jetzt von oCreateMandant() benötigt
        	
//dataSource.bOpen(ref user, ref pw).ToString();	//funzzt sogar :-)

//das funzzt nicht :-(        	
engine.oCreateMandant(ref dataSource, ref appName, ref manId, ref user, ref pw, ref hWnd, ref project, ref security);        	

Zur Erklärung: Aus irgendeinem (vermutlich zweitrangingem) Grund muss ich die Parameter alle by ref übergeben. Ansonsten sieht das ja weitenteils gleich aus. Mal abgesehen davon, dass ich oben die Verbindungsdaten an die Methode übergebe und unten die Verbindungsdaten nun in der Methode drin stehen habe.

Was aber abweicht ist die Methode oCreateMandant(). Die erfordert, wenn man sie in .NET nutzen möchte, nun noch die Parameter int hWnd, Microsoft.Office.Interop.Access._CurrentProject currentProject sowie OLSecurity security.

Insbesondere der currentProjekt Parameter macht mir Kopfschmerzen. Ich weiß nicht, was die Methode hier von mir erwartet. So wie ich es jetzt gerade oben geschrieben habe instanziere ich ja zuerst eine frische Access Instanz (was mir schon spanisch vorkommt) und übergebe dessen CurrentProjekt Eigenschaft. Das wird dann mit folgender Exception quitiert:

System.Runtime.InteropServices.COMException: In dem von Ihnen eingegebenen Ausdruck wird auf ein Objekt verwiesen, das geschlossen ist oder nicht existiert.
at OLSysEngine40.OLEngineClass.oCreateMandant(OLDataSource& oDataSource, String& sAppName, Int16& nManId, String& sBenutzer, String& sPassword, Int32& hWnd, _CurrentProject& oAccessProject, OLSecurity& oSecurity)

Leer lassen darf ich es auch nicht 😦

Ich habe keine Ahnung was ich hier noch versuchen soll. Das Problem ist sicherlich sehr speziell und ich fürchte fast, dass man ein wenig Hintergrund über die Office Line braucht um mir zu helfen...aber vielleicht hat zumindest jemand einen Tipp für mich, in welche Richtung ich ermitteln könnte.

Gruß

Christoph

383 Beiträge seit 2006
vor 14 Jahren

Im ersten Thread hast Du geschrieben, dass Du versch. Verweise im Visual Studio eingefügt hast... unter Anderem die Microsoft Access 11.0 Object Library. - Evtl. hat sich Visual Studio "verstrickt"

Ich würde mal ein frisches Projekt anlegen und NUR die Sage - DLL referenzieren, welche Du effektiv brauchst. Visual Studio wird sich dann schon melden, wenn etwas fehlt (hast ja auch schon Erfahrung damit gemacht 😃

3.728 Beiträge seit 2005
vor 14 Jahren
Missing.Value

Hallo bvsn,

in C# gibt es keine Optionalen Parameter, wie man das aus VBA kennt. Du musst immer alle Parameter angeben. Da es aber unter der Haube Optionale Parameter sind, müssen sie trotzdem nicht ausgefüllt werden. Du musst der COM-Interop-Schicht also sagen, welche Parameter du weglassen willst. Dazu benötigst Du Missing.Value, was in System.Reflection definiert ist. Das ist sozusagen ein Platzhalter für einen nicht angegebenen optionalen Parameter.

So könnte das bei Dir aussehen:


// Platzhalter-Variable erzeugen
object missing=Missing.Value;

// Das funzt einwandfrei!
engine.oCreateMandant(ref dataSource, ref appName, ref manId, ref user, ref pw, ref missing, ref missing, ref missing);

Mit der OfficeLine hat das übrigens gar nix zu tun. Das ist ein generell bekanntes COM/.NET-Interop-Problem. Bei Microsoft Office-Automatisierung hast Du genau das Selbe Problem.

Christoph Burgdorf Themenstarter:in
365 Beiträge seit 2004
vor 14 Jahren

Moin Rainbird,

verdammt! Ich kenne das Konstrukt mit Missing.Value sogar. Das braucht man ja mitunter auch, wenn man Excel Exporte macht. Ich bin aber nicht darauf gekommen, dass es hier verwendet werden muss/kann. Ich kannte den Background nicht, dass die optionalen Parameter der nativen COM DLL in der Interop Assembly auftauchen. Wieder was dazu gelernt.

Ich kann das leider erst heute Abend ausprobieren.

Vielen Dank schonmal.

Gruß

Christoph

Christoph Burgdorf Themenstarter:in
365 Beiträge seit 2004
vor 14 Jahren

Mmh..zu früh gefreut. Der Hund will das Missing.Value einfach nicht annehmen. Ich habe es jetzt sowohl so probiert:


object missingValue = Missing.Value;        	
engine.oCreateMandant(ref dataSource, ref appName, ref manId, ref user, ref pw, ref missingValue, ref missingValue, ref missingValue);
        	

Das quittiert aber direkt der Compiler mit:

cannot convert from 'ref object' to 'ref int'

als auch so:


MethodInfo mi = typeof(OLEngineClass).GetMethod("oCreateMandant");
mi.Invoke(engine, new Object[] {dataSource, appName, manId, user, pw, Missing.Value, Missing.Value, Missing.Value});
        	

Das wirft mir dann zur Laufzeit die Ausnahme:

Object of type 'System.Reflection.Missing' cannot be converted to type 'System.Int32&'

Woran kann es liegen, dass das mit der Übergabe von Missing.Value scheitert? Ich habe noch was von einer Möglichkeit mit Methodenüberladungen gelesen. Also eine Methode, die dann die optionalen Parameter weglässt und dann auf die eigentliche Methode weiterverweist. Aber das müsste ich doch dann in der COM Assembly machen und auf die habe ich ja keinen Einfluss. Oder sehe ich das falsch?

Außerdem habe ich noch von einer Möglichkeit gelesen das ganze mit VB.NET zu machen, dass wohl das Konstrukt mit optionalen Parametern unterstützt. Da wollte ich jetzt mal nach schauen...ist natürlich irgendwie...dreckig 😦

Gruß

Christoph

Christoph Burgdorf Themenstarter:in
365 Beiträge seit 2004
vor 14 Jahren

Also mit VB.NET klappt es. Weiß jetzt nicht, ob ich mich freuen oder würgen soll 😉

1.274 Beiträge seit 2005
vor 14 Jahren

Kleiner TIPP:

Mitn Reflector die C# Variante ansehen 😃

"Das Problem kennen ist wichtiger, als die Lösung zu finden, denn die genaue Darstellung des Problems führt automatisch zur richtigen Lösung." Albert Einstein

Christoph Burgdorf Themenstarter:in
365 Beiträge seit 2004
vor 14 Jahren

Hallo LastGentleman,

ich habe den Reflector noch nie wirklich benutzt. Daher weiß ich nicht, ob ich hier was entscheidendes übersehe. Aber wenn ich mir die Methode ansehe, sehe ich auch nur:


[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(0x60030003)]
public virtual extern Mandant oCreateMandant([In, Out, MarshalAs(UnmanagedType.Interface)] ref OLDataSource oDataSource, [In, Out, MarshalAs(UnmanagedType.BStr)] ref string sAppName, [In, Out] ref short nManId, [In, Out, MarshalAs(UnmanagedType.BStr)] ref string sBenutzer, [In, Out, MarshalAs(UnmanagedType.BStr)] ref string sPassword, [In, Out, Optional] ref int hWnd, [In, Out, Optional, MarshalAs(UnmanagedType.Interface)] ref _CurrentProject oAccessProject, [In, Out, Optional, MarshalAs(UnmanagedType.Interface)] ref OLSecurity oSecurity);

Sollte mir das irgendwas sagen?

Gruß

Christoph

1.274 Beiträge seit 2005
vor 14 Jahren

Das ist doch die Interopt Assembly. Hab das so gemeint, du machst es in VB.NET da gehts es ja, dann schaust du dir die Ausgabe in C# an und kannst den funktionieren Code übernehmen.

"Das Problem kennen ist wichtiger, als die Lösung zu finden, denn die genaue Darstellung des Problems führt automatisch zur richtigen Lösung." Albert Einstein

Christoph Burgdorf Themenstarter:in
365 Beiträge seit 2004
vor 14 Jahren

Jackpot! Jetzt funnzt es.

Er benötigt den Aufruf so:


int hWnd = 0;
Access._CurrentProject accessProject = null;
OLSecurity security = null;

engine.oCreateMandant(ref dataSource, ref appName, ref manId, ref user, ref pw, ref hWnd, ref accessProject, ref security);

Der Tipp mit dem Reflector war gold wert 😃

1.274 Beiträge seit 2005
vor 14 Jahren

Bist ja ein COM-Interopt Leidensgenosse da hilft man gern 😃

"Das Problem kennen ist wichtiger, als die Lösung zu finden, denn die genaue Darstellung des Problems führt automatisch zur richtigen Lösung." Albert Einstein