Plateforme Level Extreme
Abonnement
Profil corporatif
Produits & Services
Support
Légal
English
WwDotNetBridge
Message
Information générale
Forum:
Visual FoxPro
Catégorie:
Visual FoxPro et .NET
Titre:
Versions des environnements
Visual FoxPro:
VFP 9 SP2
OS:
Windows 7
Network:
Windows 2003 Server
Database:
MS SQL Server
Application:
Web
Divers
Thread ID:
01617874
Message ID:
01618226
Vues:
90
This message has been marked as a message which has helped to the initial question of the thread.
I took a quick look and i can confirm that the code does not work with the inherited class - unless you add attributes to the class. The issue is class inheritance which is (again) not directly supported by COM. Unless you mark an object [ComVisible] .NET doesn't create an extra type in the dynamic COM export and so there's no type library info available to access the properties. Using GetProperty()/SetProperty() works because it doesn't use COM access from FoxPro, but rather goes indirectly at the properties.

The other way to make this work is to ensure that [ComVisible] is set on the object you're calling. If you add:
    [ComVisible(true)]
    public class Address {}

    [ComVisible(true)]
    public class VerifiedAddress : Address {}
then the inherited properties are visible directly and you can access properties on VerifiedAddress. This is why this worked with COM initially because you had the COM attributes at the top (this may also work just by have the ComClass attribute but not quite sure)

Note that you can also specify [ComVisible] at the assembly level so all types are automatically marked ComVisible. In Properties.cs:

[assembly:ComVisible(true)]

will automatically make every class COM visible and this is a good idea if you create a custom assembly specifically for being called from FoxPro. I haven't run into this because when I create my own classes for interop I tend to set the global ComVisible flag. Also in .NET in general inheritance is not actually very much - composition is a much more popular pattern, so it's actually fairly rare to see inherited types.

Anyway, this works fine for your own code but obviously for external objects this won't necessarily work. Then again with COM Interop you can't access any objects that aren't [ComVisible] in the first place :-)

Sorry for the long message - just writing down what I found while testing this out. Point is though - you can most definitely do everything you can do with COM Interop with wwDotnetBridge. The only exception I know of that still requires COM registration is using COM events (as described in the white paper).

Aloha,

+++ Rick ---

>I don't know why it produced that error. My class was very simple and I was switching back and forth from static method and class method. This is my current test working program:
>
>
>do wwDotNetBridge
>local loBridge as wwDotNetBridge
>clear
>loBridge = createobject("wwDotNetBridge", "V4")
>if vartype(loBridge) = 'O'
>	if loBridge.LoadAssembly("C:\_Siriusware\Main\Siriusware.Library\Siriusware.Library.AddressVerification\bin\Debug\Siriusware.Library.AddressVerification.dll")
>	
>		loOriginalAddress = loBridge.Createinstance("Siriusware.Library.AddressVerification.Address")
>*		loVerifiedAddress = loBridge.Createinstance("Siriusware.Library.AddressVerification.VerifiedAddress")
>		 loOriginalAddress.Address1 ="2170 S Josephine St Unit 1&/>"
>            loOriginalAddress.Address2 = ""
>            loOriginalAddress.City =  "Denver"
>            loOriginalAddress.Country = "Bad"
>            loOriginalAddress.State = "CO"
>            loOriginalAddress.Zip = "80210"
>		
>
>*!*			loOriginalAddress = createobject('empty')
>*!*					
>*!*					addproperty(loOriginalAddress, 'Address1', "2170 S Josephine St Unit 1&/>")
>*!*					addproperty(loOriginalAddress, 'Address2', "")
>*!*					addproperty(loOriginalAddress, 'City', "Denver")
>*!*					addproperty(loOriginalAddress, 'State', "CO")
>*!*					addproperty(loOriginalAddress, 'Zip', "80210")
>*!*					addproperty(loOriginalAddress, 'Country', "BAD")
>				
>		loVerifiedAddress = loBridge.InvokeStaticMethod("Siriusware.Library.AddressVerification.VerifiedAddress", ;
>		"VerifyAddress", loOriginalAddress)
>		? loBridge.GetProperty(loVerifiedAddress, "Result")
>		? loBridge.GetProperty(loVerifiedAddress, "Zip")
>		
>
>	else
>		_cliptext = loBridge.cErrorMsg
>		? loBridge.cErrorMsg
>	endif
>endif
>
>
>and the code in the library is also very simple:
>
>
>namespace Siriusware.Library.AddressVerification
>{
>    //[ComVisible(true)]
>    //[ClassInterface(ClassInterfaceType.AutoDual)]
>    //[ProgId("SysManager.Address")]
>    public class Address
>    {
>        public string Address1 { get; set; }
>        public string Address2 { get; set; }
>        public string City { get; set; }
>        public string State { get; set; }
>        public string Zip { get; set; }
>        public string Country { get; set; }
>    }
>
>    //[ComVisible(true)]
>    //[ClassInterface(ClassInterfaceType.AutoDual)]
>    //[ProgId("SysManager.VerifiedAddress")]
>    public class VerifiedAddress : Address
>    {
>        public string Result { get; set; }
>        public string ErrorMsg { get; set; }
>
>        public static VerifiedAddress VerifyAddress(Address originalAddress)
>        {
>            AppInfo.ModuleName = "SysManager";
>
>            VerifiedAddress verifiedAddress = new VerifiedAddress();
>
>            String address = originalAddress.Address1;
>            String address2 = originalAddress.Address2;
>            String city = originalAddress.City;
>            String state = originalAddress.State;
>            String zip = originalAddress.Zip;
>            String country = originalAddress.Country;
>
>            String result = "";
>            try
>            {
>                result = Cass.VerifyAddress(ref address, ref address2, ref city, ref state, ref zip, ref country);
>                verifiedAddress.Result = result;
>                verifiedAddress.Address1 = address;
>                verifiedAddress.Address2 = address2;
>                verifiedAddress.City = city;
>                verifiedAddress.State = state;
>                verifiedAddress.Zip = zip;
>                verifiedAddress.Country = (country=="United States of America")?"USA":country;
>            }
>
>            catch (Exception ex)
>            {
>                Logging.Log(ex.ToString());
>                verifiedAddress.ErrorMsg = ex.ToString();
>            }
>
>            return verifiedAddress;
>        }       
>    }   
>}
>
>Could it be because one class is a subclass of another?
>
>
>>The COM object that comes back from wwDotnetBridge calls is the same as the one you get back from 'regular' COM Interop so the behavior should be identical. If it doesn't work with the wwDotnetBridge code it also wouldn't work with plain COM interop unless you're using a different type.
>>
>>The errors come most likely because you're accessing a type that isn't supported in FoxPro. Could be because a property value is a Long, Single number or an object is a value type or generics etc. GetProperty() gets around these issues because it simply reads the value inside of .NET and then returns only the result and fixes up the result if the type is something that FoxPro/COM usually doesn't support. With COM libraries you'll often see properties or subobjects missing - they just won't export if the type can't be translated.
>>
>>Using COM interop is fine, but you lose nothing by using wwDotnetBridge, and if you do anything more complex than basic stuff you are almost guarenteed to run into the COM limitations.
>>
>>+++ Rick ---
>>
>>>I used the bridge, but the last two lines produced unspecified COM error.
>>>
>>>I made it to work with
>>>
>>>
>>>? loBridge.GetProperty(loVerifiedAddress, "Result")
>>>		? loBridge.GetProperty(loVerifiedAddress, "Zip")
>>>
>>>>Not sure why you'd want to go the plain COM route.
>>>>
>>>> Everything that you do with COM you can also do with wwDotnetBridge using the same syntax except for object creation. But with COM you have to register the component which you don't have to do with wwDotnetBridge. That alone should be reason enough to not use COM components especially since you need to have special tools to register for COM Interop on the client machine.
>>>>
>>>>Here's your code that should work with wwDotnetBridge:
>>>>
>>>>
>>>>loBridge = GetwwDotnetBridge()
>>>>
>>>>LOCAL loOriginalAddress as SysManager.Address
>>>>LOCAL loVerifiedAddress as SysManager.VerifiedAddress
>>>>loBridge.LoadAssembly("Sysmanager.dll") && assume it's in path
>>>>loOriginalAddress = loBridge.CreateInstance("SysManager.Address")
>>>>loOriginalAddress.Address1 ="2170 S Josephine St Unit 1&/>"
>>>>loOriginalAddress.Address2 = ""
>>>>loOriginalAddress.City =  "Denver"
>>>>loOriginalAddress.Country = "Bad"
>>>>loOriginalAddress.State = "CO"
>>>>loOriginalAddress.Zip = "80210"
>>>>
>>>>loVerifiedAddress = loBridge.CreateInstance("SysManager.VerifiedAddress")
>>>>loVerifiedAddress = loVerifiedAddress.VerifyAddress(loOriginalAddress)
>>>>?loVerifiedAddress.Result
>>>>?loVerifiedAddress.Country
>>>>
>>>>
>>>>Other than LoadAssembly and loBridge.CreateInstance() you get to use the same exact code - minus the COM registration requirement.
>>>>
>>>>+++ Rick ---
>>>>
>>>>>Hi Rick,
>>>>>
>>>>>Based on your excellent article that is provided with the wwDotNetBridge I actually decided to try a simpler route now without a bridge. I build a wrapper .NET library, I am able to run the code on my development machine like this
>>>>>
>>>>>
>>>>>LOCAL loOriginalAddress as SysManager.Address
>>>>>LOCAL loVerifiedAddress as SysManager.VerifiedAddress
>>>>>loOriginalAddress = CREATEOBJECT("SysManager.Address")
>>>>>loOriginalAddress.Address1 ="2170 S Josephine St Unit 1&/>"
>>>>>loOriginalAddress.Address2 = ""
>>>>>loOriginalAddress.City =  "Denver"
>>>>>loOriginalAddress.Country = "Bad"
>>>>>loOriginalAddress.State = "CO"
>>>>>loOriginalAddress.Zip = "80210"
>>>>>
>>>>>
>>>>>loVerifiedAddress = CREATEOBJECT("SysManager.VerifiedAddress")
>>>>>
>>>>>loVerifiedAddress = loVerifiedAddress.VerifyAddress(loOriginalAddress)
>>>>>
>>>>>?loVerifiedAddress.Result
>>>>>
>>>>>?loVerifiedAddress.Country
>>>>>
>>>>>So, the problem will be to register that component for our clients - I am wondering if I should use your RegisterDotNetComponent from the older article as is?
>>>>>
>>>>>Thanks again.
>>>>>
>>>>>>It's already released on Github...
>>>>>>
>>>>>>https://github.com/RickStrahl/wwDotnetBridge
>>>>>>
>>>>>>Don't forget to star the repro if you use the code.
>>>>>>
>>>>>>+++ Rick ---
>>>>>>
>>>>>>>Wow, that would be great when supported. Do you know when you're going to release this new version (sorry for the rush)?
>>>>>>>
>>>>>>>I can either ask my colleague for the wrapper method or wait for that new version of the bridge.
>>>>>>>
>>>>>>>Thanks a lot again.
>>>>>>>
>>>>>>>>This is currently not supported. You can only use ByRef parameters if you're calling .NET directly.
>>>>>>>>
>>>>>>>>I'm adding this functionality as we speak and it'll look like this:
>>>>>>>>
>>>>>>>>
>>>>>>>>loNet = loBridge.Createinstance("Westwind.WebConnection.TypePassingTests")
>>>>>>>>
>>>>>>>>*** Create ComValue objects for each parameter
>>>>>>>>loInt = loBridge.CreateComValue()
>>>>>>>>loInt.Value = INT(10)
>>>>>>>>loString = loBridge.CreateComValue()
>>>>>>>>loString.Value = "Hello World."
>>>>>>>>loDecimal = loBridge.CreateComValue()
>>>>>>>>loDecimal.Value = CAST( 5.22 as Currency)
>>>>>>>>
>>>>>>>>lobridge.InvokeStaticMethod("Westwind.WebConnection.TypePassingTests",;
>>>>>>>>                            "PassByReferenceStatic",;
>>>>>>>>                            loInt,loString,loDecimal)
>>>>>>>>
>>>>>>>>*** Look at the result values
>>>>>>>>? loInt.Value, loString.Value, loDecimal.Value
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>+++ Rick ---
>>>>>>>>
>>>>>>>>
>>>>>>>>>UPDATE. I checked your code and you're passing parameters as is, not by reference. I am wondering if I change it to pass by reference, my problem will be resolved.
>>>>>>>>>
>>>>>>>>>UPDATE 2. Just changing them in the wwDotNetBridge program to be passed by reference didn't help.
>>>>>>>>>
>>>>>>>>>Hi Rick,
>>>>>>>>>
>>>>>>>>>I have another question. The signature for the method is this
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>static public string VerifyAddress( ref string AddressLine1, ref string AddressLine2, ref string City, ref string State, ref string ZipCode, ref string Country)
>>>>>>>>>
>>>>>>>>>So, although it may be not a good practice, my colleague (who wrote this method) returns new values using parameters passed by references. When I debug that DLL, I can see the new values, however, my call
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>lcResult = loBridge.Invokestaticmethod("Siriusware.Library.Cass", "VerifyAddress", @Address1, @Address2, @City, @State, @Zipcode, @Country)
>>>>>>>>>		?Address1
>>>>>>>>>		?ZipCode
>>>>>>>>>		
>>>>>>>>>		?lcResult
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>does not return the new values for the address components.
>>>>>>>>>
>>>>>>>>>Do you know what needs to be done to be able to get the changed values in VFP or how the method should be changed in C# if this is not possible?
>>>>>>>>>
>>>>>>>>>Thanks in advance.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>>>UPDATE. I got it to work using InvokeStaticMethod, e.g. I'm getting the result. The only problem is that this dll reads one setting from the ini file and it's not finding that ini file and therefore the result is not what I want to get.
>>>>>>>>>>
>>>>>>>>>>Do you mean the YourExe.config file? If so that file needs to be with the executing EXE. So if you're running in Fox IDE it'll be vfp.exe.config. If you're running your actual exe it'll be YourExe.exe.config.
>>>>>>>>>>
>>>>>>>>>>+++ Rick ---
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>Hi everybody,
>>>>>>>>>>>
>>>>>>>>>>>I am trying to use wwDotNetBridge to access static class from our custom dll. I am trying this code:
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>do wwDotNetBridge
>>>>>>>>>>>local loBridge as wwDotNetBridge
>>>>>>>>>>>loBridge = createobject("wwDotNetBridge", 'V4')
>>>>>>>>>>>if vartype(loBridge) = 'O'
>>>>>>>>>>>	if loBridge.LoadAssembly("Siriusware.Library.dll")
>>>>>>>>>>>
>>>>>>>>>>>		Address1 = "2170 S Josephine St Unit 1"
>>>>>>>>>>>		Address2 = ""
>>>>>>>>>>>		City = "Denver"
>>>>>>>>>>>		State = "CO"
>>>>>>>>>>>		Zipcode  = "80210"
>>>>>>>>>>>		Country = "USA"
>>>>>>>>>>>
>>>>>>>>>>>		*            string sResult = Cass.VerifyAddress( ref Address1, ref Address2, ref City, ref State, ref Zipcode, ref Country);
>>>>>>>>>>>
>>>>>>>>>>>		loCass = loBridge.CreateInstance("Cass")
>>>>>>>>>>>		if isnull(loCass)
>>>>>>>>>>>			? loBridge.cErrorMsg
>>>>>>>>>>>			return
>>>>>>>>>>>		endif
>>>>>>>>>>>
>>>>>>>>>>>		*loPop.Connect("mail.gorge.net",587,.f.)
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>		* loPop.Connect("mail.gorge.net",587,.f.)
>>>>>>>>>>>		*? loBridge.InvokeMethod(loPop,"Connect","pop3.gorge.net",110,.f.)
>>>>>>>>>>>
>>>>>>>>>>>		? loCass.VerifyAddress(@Address1, @Address2, @City, @State, @Zipcode, @Country)
>>>>>>>>>>>	endif
>>>>>>>>>>>endif
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>but I am getting this strange error
>>>>>>>>>>>
>>>>>>>>>>>Type not loaded. Please load call LoadAssembly first.
>>>>>>>>>>>
>>>>>>>>>>>What do I need to do in order to use the class and why do I get this weird error message if I did use LoadAssembly already?
+++ Rick ---

West Wind Technologies
Maui, Hawaii

west-wind.com/
West Wind Message Board
Rick's Web Log
Markdown Monster
---
Making waves on the Web

Where do you want to surf today?
Précédent
Suivant
Répondre
Fil
Voir

Click here to load this message in the networking platform