Laden...

COM-Methode undefined, COM-Object wurde mit C#, InterOp und Visual Studio erstellt

Erstellt von Urbin Jürgen vor 16 Jahren Letzter Beitrag vor 16 Jahren 6.924 Views
U
Urbin Jürgen Themenstarter:in
5 Beiträge seit 2007
vor 16 Jahren
COM-Methode undefined, COM-Object wurde mit C#, InterOp und Visual Studio erstellt

Hallo,

ich versuche mit C# ein COM-Object zu erstellen, auf deren Methoden und Properties ich von einer HTML-Seite mit Hilfe von JavaScript zugreifen kann.

Nach ettlichen Stunden Internet-Recherche habe ich eine fehlerfreie dll und tlb erzeugt. In der tlb-Datei sind mein Interface mit der für COM freigegebenen Methode readCard() und das Property snrLegic enthalten. Wenn ich allerdings versuche von der HTML-Seite auf das Object zuzugreifen wird die DLL installiert und in javascript zwar das Object gefunden, aber bei der Methode kommt der javaScript-Fehler "Das Object unterstützt diese Eigenschaft oder Methode nicht". Beim Zugriff auf das Property kommt einfach "undefined" zurück.

Nachfolgend ein Auszug aus dem C# - Code des COM-Objectes:


using System.Runtime.InteropServices;
using System.Text;
using System.Reflection;
using Microsoft.Win32;
using RexControls;

namespace LegicSmartCard
{

    [ComVisible(true)]
    [Guid("491889F5-FE76-48f1-8914-38E61F80BAD4")]
    public interface ILegicSmartCardControl 
    {
        [ComVisible(true)]
        [DispId(1)]
        void readCard();
        [ComVisible(true)]
        [DispId(2)]
        string snrLegic { get; set; }
    }

    [ProgId("LegicSmartCard.LegicSmartCardControl")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComVisible(true)]
    [Guid("0BE78345-0612-4322-ACAA-25145312DE14")]
    public class LegicSmartCardControl : Component,   LegicSmartCard.ILegicSmartCardControl
    {
        [ComVisible(true)]
        public string snrLegic
            {
            get
            {
            return snrLegic;
            }
            set
            {
            snrLegic = value;
            }
            }

        public LegicSmartCardControl()
		{
		}

	protected override void	Dispose( bool disposing	)
		{
			base.Dispose( disposing	);
		}

	///	Called to register the control
	[ComRegisterFunction()]
	public static void RegisterClass ( string key )
		{
		// Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it
		StringBuilder	sb = new StringBuilder ( key ) ;
		sb.Replace(@"HKEY_CLASSES_ROOT\","") ;

		// Open the CLSID\{guid} key for write access
		RegistryKey k	= Registry.ClassesRoot.OpenSubKey(sb.ToString(),true);

		// And create	the	'Control' key -	this allows	it to show up in
		// the ActiveX control container
		RegistryKey ctrl = k.CreateSubKey	( "Control"	) ;
		ctrl.Close ( ) ;

		// Next create the CodeBase entry	- needed if	not	string named and GACced.
		RegistryKey inprocServer32 = k.OpenSubKey	( "InprocServer32" , true )	;
		inprocServer32.SetValue (	"CodeBase" , Assembly.GetExecutingAssembly().CodeBase )	;
		inprocServer32.Close ( ) ;

		// Finally close the main	key
		k.Close (	) ;
		}

	///	Called to unregister the control
	[ComUnregisterFunction()]
	public static void UnregisterClass ( string	key	)
		{
		StringBuilder	sb = new StringBuilder ( key ) ;
		sb.Replace(@"HKEY_CLASSES_ROOT\","") ;

		// Open	HKCR\CLSID\{guid} for write	access
		RegistryKey	k =	Registry.ClassesRoot.OpenSubKey(sb.ToString(),true);

		// Delete the 'Control'	key, but don't throw an	exception if it	does not exist
		k.DeleteSubKey ( "Control" , false ) ;

		// Next	open up	InprocServer32
		RegistryKey	inprocServer32 = k.OpenSubKey (	"InprocServer32" , true	) ;

		// And delete the CodeBase key,	again not throwing if missing
		k.DeleteSubKey ( "CodeBase"	, false	) ;

		// Finally close the main key
		k.Close	( )	;
		}

        [ComVisible(true)]
        public void readCard() 
                {
                 //... Programmcode 
                 snrLegic = "Testausgabe"; 
                }

       }
}

Und hier meine HTML-Testseite:


<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
    <title>Seriennummer auslesen</title>
    <script language="javascript" >
         function readSeriennr()
         {
         var ocx=document.getElementById("LegicSmartCard");
         var feld1=document.getElementById("textfeld");
         if(!ocx) {
              feld1.value = "Object nicht gefunden!" ;	
	      return;
         }
         ocx.readCard();
         feld1.value = 	ocx.snrLegic; 
         }
     </script> 
</head>
<body>
     <h1>Seriennummer auslesen</h1>
     Seriennummer:
     <form action="test.htm">
	   <textarea cols="20" rows="4" name="textfeld"></textarea>
	   <input type="button" name="readSnr" value="Seriennummer lesen" onclick="readSeriennr()" />
     </form>
<object style="display:none"  id="LegicSmartCard" codebase="legicSmartCard.dll" classid="CLSID:491889F5-FE76-48F1-8914-38E61F80BAD4"/>
</body>
</html>

Die Entwicklungsumgebung war Visual Studio 2005 das Project erstellt eine Klassenbibliothek mit COM-InterOp aktiviert. In der Assembly sind folgende Parameter eingestellt.

[assembly: AssemblyVersion("1.0.999")]
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("..\..\LegicSmartCard.snk")]
[assembly: AssemblyKeyName("")]
[assembly: AssemblyFileVersionAttribute("1.0.999")]
[assembly: ComVisible(true)]

Das KeyFile wurde mit SN.exe erstellt.

Für jede Hilfe schon mal im vorraus Danke!

3.728 Beiträge seit 2005
vor 16 Jahren
Dispatch-Unterstützung

Du hast etwas entscheidendes vergessen. VbScript und javascript können nur über späte Bindung (also via Dispatch-Schnittstelle) auf COM-Objekte zugreifen. Du hast die Erzeugung einer Dispatch-Schnittstelle aber abgeschaltet. Die COM-Register-Funktionen sind absolut unnötig. Stattdessen solltest Du das ClassInterface-Attribut folgendermaßen abändern:

[ClassInterface(ClassInterfaceType.AutoDispatch)]

Wenn Du deine COM-Komponente nun neu erstellst und registrierst (via Visual Studio oder REGASM), sollte auch Javascript keine Probleme mehr damit haben.

U
Urbin Jürgen Themenstarter:in
5 Beiträge seit 2007
vor 16 Jahren

Danke Rainbird,

für die schnelle Antwort!
Ich habe diese Änderungen am Code vorgenommen. Das heißt wo ClassInterface auf NONE gesetzt wurde, setze ich es jetzt auf AutoDispatch und die beiden überflüssigen Methoden zur ComRegistierung habe ich entfernt. Leider hat sich an der Situation nichts geändert!

Als WebServer benutze ich den bei Windows XP Professional mitgelieferten IIS. Im Standardverzeichnis des Webservers habe ich die HTML-Datei mit dem Javascript, wie auch die Ergebnisse des Compiliervorgangs (*.dll, *.tlb, *.pdb, *.vshost.exe) auf dem Visual Studio abgelegt.

Eigentlich bin ich davon ausgegangen, daß das VisualStudio das Com-Object schon registiert, aber nachdem der gleiche Fehler wieder auftrat habe ich, vom Standardverzeichnis des Webservers aus, zu erst regasm /u <DLL> und anschließend regasm <DLL> aufgerufen. Dies hat an der Fehlersituation nichts verändert.

Getestet habe ich mit dem IE7 auf dem gleichen Rechner, auf dem auch der IIS läuft.
Mache ich beim Zugriff auf das Object aus der HTML-Datei etwas verkehrt?

3.728 Beiträge seit 2005
vor 16 Jahren
Regasm

Du musst REGASM folgendermaßen ausführen:

REGASM MyAssembly.dll /codebase /tlb:MyAssembly.tlb

Damit wird auch der absolute Pfad zur DLL (Codebasis) in der COM-Registrierung hinterlegt und eine Typenbibliothek erzeugt. Ansonsten findet das COM-Ladeprogramm die Assembly nicht, wenn sie nicht im GAC liegt.

3.728 Beiträge seit 2005
vor 16 Jahren
Object-Tag

Moment mal! Du verwendest ja einen Object-Tag um die COM-Komponente zu erzeugen. Das geht soweit ich weiss nur mit ActiveX-Controls und ActiveX-Dokumenten, aber nicht mit herkömmlichen COM-Bibliotheken. Du solltest das COM-Objekt direkt in JavaScript erzeugen. Den Object-Tag brauchst Du dann nicht mehr. Das geht so:


// Instanz erzeugen
var smartCard=new ActiveXObject('LegicSmartCard.LegicSmartCardControl'); 

// Karte lesen
smartCard.readCard;

U
Urbin Jürgen Themenstarter:in
5 Beiträge seit 2007
vor 16 Jahren

Hallo Rainbird,

das mit der Instanziierung eines neuen ActiveXObjectes habe ich auf schon versucht, allerdings habe ich dann einen StackOverflow-Fehler des IE7 bekommen. Er bietet mir dann den Debugger an und verweißt darauf, daß die get oder set-Methode des Proberties keine StackOverFlow-Behandlung hätte. Alles etwas dubios.

Aber das mit dem geänderten regasm-Aufruf werden ich gleich mal ausprobieren!

U
Urbin Jürgen Themenstarter:in
5 Beiträge seit 2007
vor 16 Jahren

Habe noch eine Information vergessen!

Der StackOverFlow kommt erst wenn man den WarnHinweis des IE7 mit folgendem Text

"Ein ActiveX-Steuerelement dieser Seite ist möglicherweise in Wechselwirkung mit anderen Elementen dieser Seite nicht Sicher. Möchten Sie dies zulassen?"

mit JA beantwortet! Anwortet man mit Nein kommt nur die Meldung:

"Fehler: Automatisierungsserver kann Object nicht erstellen!"

Da ich kein weiteres Steuerelement in meiner HTML-Seite eingebunden habe ist der Warnhinweis schon schwer zu Verstehen. Der StackOverFlow ist mir allerdings ein Rätsel, da in meinem JavaScript und auch im Com-Object keine Schleifen enthalten sind , welche endlos laufen können. Auch sind mir keine rekursive-Aufrufe bekannt.

U
Urbin Jürgen Themenstarter:in
5 Beiträge seit 2007
vor 16 Jahren

Hallo,

habe statt des IE7 den Firefox verwendet. Dieser bringt keine StackOverFlow-Meldung, sondern meldet nur das das Object 'undefined' ist (e.description='undefined').


function readSeriennr()
{
var ocx;
try 
   {
   ocx  = new ActiveXObject('LegicSmartCard.LegicSmartCardControl');
   }
catch(e) 
   {
   if ((!e) && (ocx)) 
      alert('LegicSmartCard installiert!');
   else 
      alert('Fehler: ' + e.description );
   }
//...
}

Als weiteres Problem in diesem Zusammenhang habe ich festgestellt, daß die HTML-Seite von einem anderen Rechner aus aufgerufen, eine andere Fehlermeldung bringt. Der dort installierte IE6 meldete nur das das Object nicht gefunden wurde. Wie läuft die Registierung des COM-Objects bei entfernten Rechner, welche nur über einen Browser auf die WEB-Seite und damit auf das COM-Object zugreifen?

3.728 Beiträge seit 2005
vor 16 Jahren
Testen

Hast Du Deine COM-Komponente auch mal ganz ohne Browser getestet? Z.B. mit einem kleinen VBScript:

' C:\Test.VBS
Dim objSmartCard
Set objSmartCard=CreateObject("LegicSmartCard.LegicSmartCardControl")
objSmartcard.readCard

Wenn bei diesem Skript auch ein Fehler kommt, hat das mit dem Browser gar nix zu tun, sondern es ist ein Bug in Deinem Code.