# Active-X - DLL mit JNA/JNI/JACOB



## krischans (11. Mrz 2010)

Hallo!

Ich hab zwar noch nicht allzu viel mit Java zu tun gehabt, hab mich aber relativ schnell rein gefunden.
Jetzt habe ich allerdings ein "mittel-schweres" Problem.
Ich möchte eine Java-Tool schreiben, mit dem ich auf eine AMX-Mediensteuerungsanlage zugreifen kann. Die Anbindung erfolgt über eine Active-X Schnittstelle.
Ich habe mit schon mit JACOB beschäftigt und weiß, dass ich damit die Brücke zwischen Java und Active-X herstellen kann, ich weiß auch wie das funktioniert. 
Das Problem an der ganzen Sache ist, dass ich nur eine *.DLL vorliegen habe, in der alle Funktionen der Anlage hinterlegt sind. 
Ich weiß nicht, wie ich diese DLL in mein Projekt lade und wie ich dann auf die Funktionen zugreifen kann. 
Ich hab es auch schon mit JNI bzw. JNA versucht, da wird die DLL zwar eingebunden, aber da dies ja nur aktiv zur Laufzeit geschieht, kann ich während der Programmierung eben nicht auf die Funktionen zu greifen.
Wie kann ich das nun machen?
Wie gesagt die DLL ist Active-X.
Gibt es irgendeine Möglichkeit diese als Java-Bibliothek einzubinden, so dass ich dann mit Hilfe von Jacob damit arbeiten kann?

Würde mich freuen, wenn mir da jemand helfen könnte. 
Und das mir dann auch gleich so erklärt, dass/wie man das in NetBeans machen kann.

Vielen Dank im Voraus
KrischanS


----------



## tuxedo (12. Mrz 2010)

Nimm die Eclipse SWT OleBridge. Das funktioniert prima. Haben wir hier produktiv im großen Stil im Einsatz.

Hier mal ein Link zum einlesen: Eclipse Corner Article: ActiveX Support In SWT

Die meisten Artikel beziehen sich auf ActiveX Komponenten wie Word und Excel. Aber das lässt sich auf auf jede beliebige andere ActiveX Komponente abbilden.

- Alex


----------



## krischans (12. Mrz 2010)

Danke! werd mich da mal belesen!

Irgendwie komme ich damit allerdings nicht klar... 
Wie muss ich denn die DLL einbinden? Ich habe dies Tutorial ausprobiert, nur weiß ich immer noch nicht, wie ich dann diese DLL richtig einbinde...

oder gibt es ne komplett andere Lösung? 
Ich hab z.B. ein fertiges C++-Tool das mir die Funktionen ausführt, aber das ganze soll ja zu Java werden... besteht da irgendwie die Möglichkeit das C-Programm zu straten und dann auf dessen Methoden/Funktionen zu zugreifen?


----------



## tuxedo (12. Mrz 2010)

ActiveX Controls werden im System mit einem Namen registriert. In deinem Programm referenzierst du dein ActiveX über diesen Namen. SWT schaut dann intern nach welche DLL (das geht über den Namen über die Registry) dafür zuständig ist.

Fertig.

- Alex


----------



## krischans (15. Mrz 2010)

Hallo tuxedo!

Geht das ganze denn auch in Netbeans?
Das Problem ist, soweit ich jetzt dahinter gestiegen bin, dass die DLL kein Formular enthält. Denn wenn ich das richtig verstanden habe wird ja mit diesem Tutorial ein Formular oder GUI o.ä. aufgerufen, das in der DLL hinterlegt ist. Diese DLL hat allerdings nur Funktionen...
Ich hab hier mal den Quelltext:


```
package test;

import org.eclipse.swt.*;
import org.eclipse.swt.internal.ole.win32.COM;
import org.eclipse.swt.internal.ole.win32.COMObject;
import org.eclipse.swt.internal.ole.win32.GUID;
import org.eclipse.swt.internal.ole.win32.IDispatch;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.ole.win32.*;
import org.eclipse.swt.widgets.*;
import java.io.*;

public class Main {
	
	public static void main(String[] args) {
	  
	  Display display = new Display();
	  Shell shell = new Shell(display);
	  OleFrame frame = new OleFrame(shell, SWT.NONE);

	  
	  Menu bar = new Menu(shell, SWT.BAR);
	  shell.setMenuBar(bar);
		
	  MenuItem fileItem1 = new MenuItem(bar, SWT.CASCADE);
	  fileItem1.setText("&File_Item_1");
	  MenuItem fileItem2 = new MenuItem(bar, SWT.CASCADE);
	  fileItem2.setText("&File_Item_2");

	  MenuItem containerItem = new MenuItem(bar, SWT.CASCADE);
	  containerItem.setText("&Container_Item");

	  MenuItem windowItem1 = new MenuItem(bar, SWT.CASCADE);
	  windowItem1.setText("&Window_Item_1");
	  MenuItem windowItem2 = new MenuItem(bar, SWT.CASCADE);
	  windowItem2.setText("&Window_Item_2");

	  frame.setFileMenus(new MenuItem[] {fileItem1, fileItem2});
	  frame.setContainerMenus(new MenuItem[] {containerItem});
	  frame.setWindowMenus(new MenuItem[] {windowItem1, windowItem2});
	  
	  	  

	try{
 	OleControlSite controlsite = new OleControlSite(frame, SWT.NONE, "PCLinkPlus.PCLink.1");
	...	


	....	

	  controlsite.deactivateInPlaceClient();
	  controlsite.dispose();
  }
}
```


Als Fehlermedung taucht folgendes auf:


```
Exception in thread "main" org.eclipse.swt.SWTException: Failed to create Ole Client. result = -2147467262
	at org.eclipse.swt.ole.win32.OLE.error(OLE.java:302)
	at org.eclipse.swt.ole.win32.OleControlSite.<init>(OleControlSite.java:142)
	at test.Main.main(Main.java:50)
```

Vielleicht kannst du damit ja was anfangen...

Krischans


----------



## tuxedo (15. Mrz 2010)

Wir benutzen ActiveX Controls ausschließlich "formularlos". 
Muss deinen Code mal mit unserem vergleichen. Vllt. kann ich dir auch ein kleines Sampel stricken. Mal schauen.


----------



## tuxedo (15. Mrz 2010)

So in etwa sieht das bei uns aus:


```
public class MyOleControlSite extends OleControlSite {
	
	private static OleFrame oleFrame;
    private static Shell sshell;
	private OleAutomation mAutomation;
	private MyStateEventListener listener = new StateChangeEventListener();
	
	/**
     * Event ID for state change events. This ID is defined in/with activeX control
     */
    private static final int STATECHANGE_EVENTS = 0x00000002;
	
	private class MyStateEventListener implements OleListener {
        
        /**
		 * Just a small example event listener. It receives ActiceXControl events, read the event data and 
		 * forwards the "stuff" via UI thread asynchronously
		 *
         * @see org.eclipse.swt.ole.win32.OleListener#handleEvent(org.eclipse.swt.ole.win32.OleEvent)
         */
        @Override
        public void handleEvent (OleEvent anEvent)
        {
            final String controlId = anEvent.arguments[0].getString();
            final int state = anEvent.arguments[1].getInt();
                        
            // decouple events
            sshell.getDisplay().asyncExec(new Runnable(){

                @Override
                public void run ()
                {
					// do something with controlID and state ...i.e. forward to other event handler logic
                }
                
            });
        }
        
    }	
	
	public MyOleControlSite (Composite parent, int style) {
	
		super(parent, style, "MyActiveXControl.ControlName");
		sshell = new Shell();
        sshell.setText("MyOleCOntrolSite Shell");
        sshell.setVisible(false);
        sshell.setSize(800, 600);
        sshell.setLayout(new FillLayout());
        
        // create container to embed the ActiveX
        oleFrame = new OleFrame(sshell, SWT.None);
        oleFrame.setSize(800, 600);
        oleFrame.setLayout(new FillLayout());
		
		doVerb(OLE.OLEIVERB_SHOW);
        mAutomation = new OleAutomation(this);
		
		addEventListener(STATECHANGE_EVENTS, listener);
		
	}
	
	public String invokeXYZ() {
        
        
        final String dispatchName = "methodXYZ";
               
        
        int[] ids = mAutomation.getIDsOfNames(new String[]{dispatchName});
        
        if (ids.length != 1)
            throw new UnsupportedOperationException("Unknown method for ActiveX control: " + dispatchName);      
        
        String invocationResult = "<unknown>";
        invocationResult = mAutomation.invoke(ids[0]).getString();        

        return invocationResult;
    }
	
	    /**
     * @see org.eclipse.swt.widgets.Widget#dispose()
     */
    @Override
    public void dispose ()
    {
        super.dispose();
		removeEventListener(STATECHANGE_EVENTS, listener);
        mAutomation.dispose(STATECHANGE_EVENTS, new StateChangeEventListener());
    }

}
```

Hab das einfch schnell zusammenkopiert. Kann sein dass es hier und da einen kleinen Fehler enthält. Aber im großen und ganzen ist das die Vorgehensweise. 

Die Event-ID kannst du normalerweise der IDl-Beschreibung des ActiveX Controls entnehmen. invokeXYZ zeigt exemplarisch wie man Methoden auf dem Control aufruft und den Rückgabewert ausliest.
Da ActiveX Controls immer in einem Fenstercontainer leben müssen, wird das ganze in einen OleFrame gepackt. Wenn du allerdings nix hast was sichtbar gemacht werden muss, kannst du das Fenster, wie in meinem Beispiel, auch unsichtbar machen (
	
	
	
	





```
setVisible(false)
```
). 

Und ja: Man kann SWT auch in Verbindung mit der NetBeans IDE nutzen  

- Alex


----------



## krischans (15. Mrz 2010)

muss noch mal nerven... SORRY 

Ich verstehe dein Beispiel nicht so ganz, bzw. weiß ich nicht, wie ich das bei mir umsetzen kann.
Wie funktioniert die Sache mit dem "Listener"? 
Ich merk schon, ich hab von JAVA wohl doch noch nicht allzuviel Ahnung... 
hier...

```
super(parent, style, "MyActiveXControl.ControlName");
```
... setze ich doch meine DLL ein?!

hier...

```
final String dispatchName = "methodXYZ";
```
... setze ich doch die Methode aus der DLL ein richtig?!

Ach ja und was ist das hier?!

```
private MyStateEventListener listener = new MyStateEventListener();
```
muss ich da ne extra Klasse "MyState..Listenser()" anlegen?

Wäre echt nett, wenn du mir das mal "für  Idioten" erklären kannst ???:L

Danke schon mal!!!
Krischans


----------



## tuxedo (15. Mrz 2010)

> hier...... setze ich doch meine DLL ein?!



Nicht den Dateinamen der DLL, sondern den Namen des ActiveX Controls, welches in der DLL enthalten ist. Bin auch kein C/C++ Guru. Aber ActiveX Controls werden über Namen referenziert (keine Dateimnamen!). Über die Windows Registry wird dann intern nach der DLL-Datei passend zu diesem ActiveX Namen gesucht. Control-Name und Dateiname können komplett unterschiedlich sein und haben erstmal nix miteinander gemeinsamt (können, müssen aber nicht). 




> hier...... setze ich doch die Methode aus der DLL ein richtig?!



Richtig. Hier setzt du den Namen der Methode ein die du auf dem ActiveX aufrufen willst.

Die Sache mit den Listenern:
Naja. Wenn du ein ActiveX Control bastelst, dann kann das natürlich Events auslösen. Auf Java-Seite wird mit sogenannten Listenern auf diese Events gelauscht. Mehr "magie" steckt da nicht dahinter. 

- Alex

P.S.


> Ich hab zwar noch nicht allzu viel mit Java zu tun gehabt, hab mich aber relativ schnell rein gefunden.
> Jetzt habe ich allerdings ein "mittel-schweres" Problem.



Mittel-Schwer ist hier denke ich noch etwa suntertrieben. Das ist selbst für etwas fortgeschrittene nicht trivial.


----------



## krischans (15. Mrz 2010)

muss ich diesen listener selbst schreiben?

```
public MyOleControlSite (Composite parent, int style) {

      super(parent, style, "PCLinkPlus.PCLink.1");....
```

hier bringt er immer nen Fehler, irgendein Problem mit dem "super":

```
invalid method declaration; return type required
    public MyOleControlSite (Composite parent, int style) {
```


----------



## tuxedo (15. Mrz 2010)

Ähm, das ist der Konstruktor. Der heisst genau so wie die Klasse. Wenn deine Klasse anders heisst als MyOleControlSite, dann musst du den Konstruktur entsprechend anpassen.. Tust du das nicht, geht er von einer Methode aus. Und Methoden müssen entweder "void" sein, oder sie haben einen bestimmten Rückgabewert. Wird keins von beidem definiert, so beschwert er sich mit genannter Fehlermeldung. Und der Aufruf von 
	
	
	
	





```
super()
```
 funktioniert dann natürlich auch nicht. 

*puuh* wenns schon am Konstruktor hapert, dann seh ich schwarz für eine erfolgreiche ActiveX Integration in Java innerhalb der nächsten 4-8 Wochen ...

- Alex


----------



## krischans (15. Mrz 2010)

na du machst mir ja Mut


----------



## tuxedo (15. Mrz 2010)

Was erwartest du?

Ein KFZ-Lehrling tut sich in der ersten Ausbildungswoche auch extrem schwer wenn's an den Austausch eines Porsche-Motors geht. Beim guten alten VW-Käfer ist die technik halt um einiges leichter/einfacher und besser geeignet zum dran lernen. Das ist nunmal so. 

- Alex


----------



## krischans (15. Mrz 2010)

ich versteh dich schon....


----------

