FoxPro and .NET Interop
wwDotNetBridge createinstance not firing
Gravatar is a globally recognized avatar based on your email address. wwDotNetBridge createinstance not firing
  Matthew Olson
  All
  Jun 10, 2020 @ 12:25pm

Teach me the ways of the force, kind masters. I thought I had this set up correctly and created a dll written in c#. Then I used wwDotNetBridge to tunnel into my dll and fire what I needed to (an SMS message to Twilio). Here is what I’m doing in the VFP command window:

LOCAL loBridge as wwDotnetBridge
SET PROCEDURE TO wwdotnetbridge
DO wwDotnetBridge
loBridge = GetwwDotnetBridge()
loBridge.LoadAssembly("TwiliocSharp.dll")
lcerror=loBridge.createinstance("SendMessage","AuthCode","AuthToken","hi","+1Twilio#","+1Recipient#")
?lcerror

.NULL. is the result of the last line.

Here is my code for the c# dll:

using System;
using System.IO;
using System.Net;
using Twilio;
using Twilio.Rest.Api.V2010.Account;
using Twilio.TwiML.Messaging;
 
namespace TwilioCSharp
{
    public class Class1
    {
        public string sendMessage(string pcAccount, string pcToken, string pcBody, string pcFrom, string pcTo)
        {
            string accountSid = pcAccount;
            string authToken = pcToken;
 
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
 
            TwilioClient.Init(accountSid, authToken);
 
            var message = MessageResource.Create(
                body: pcBody,
                from: new Twilio.Types.PhoneNumber(pcFrom),
                to: new Twilio.Types.PhoneNumber(pcTo)
            );
 
            using (StreamWriter w = File.AppendText("log.txt"))
            {
                Log("response status code: " + TwilioClient.GetRestClient().HttpClient.LastResponse.StatusCode, w);
                Log("response body: " + TwilioClient.GetRestClient().HttpClient.LastResponse.Content, w);
            }
 
            //return message.ErrorMessage;
            return message.Status.ToString();
        }
 
        public static void Log(string logMessage, TextWriter w)
        {
            w.Write("\r\nLog Entry : ");
            w.WriteLine($"{DateTime.Now.ToLongTimeString()} {DateTime.Now.ToLongDateString()}");
            w.WriteLine($"  :{logMessage}");
            w.WriteLine("-------------------------------");
        }
    }
}

While .NULL. is a valid result, it does mean that the message failed to send. The problem is that even in that case, log.txt should show up with details of why it failed. That isn’t happening. It isn't even being created. Any ideas as to why?

Thank you for your help in advance!

Thanks, Matthew Olson

Gravatar is a globally recognized avatar based on your email address. wwDotNetBridge createinstance not firing
  Rick Strahl
  Matthew Olson
  Jun 10, 2020 @ 02:41pm

You need to create an instance first then call the method:

loTwilio = loBridge.CreateInstance("TwilioSharp.Class1")
llResult = loTwilio.SendMessage("AuthCode","AuthToken","hi","+1Twilio#","+1Recipient#")

Since the method is simple you should be able to call it directly.

The result shouldn't be null since the result is a string.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge createinstance not firing
  Matthew Olson
  Rick Strahl
  Jun 11, 2020 @ 11:02am

That makes sense. I've adjusted my calls to:

LOCAL loBridge as wwDotnetBridge
SET PROCEDURE TO wwdotnetbridge
DO wwDotnetBridge
loBridge = GetwwDotnetBridge()
loBridge.LoadAssembly("TwiliocSharp.dll")
loTwilio = loBridge.CreateInstance("TwilioSharp.Class1")
llResult = loTwilio.SendMessage("AuthCode","AuthToken","hi","+1Twilio#","+1Recipient#")

I get an error when doing the last line: LOTWILIO is not an object. So, I'm still doing something wrong somewhere. Anymore ideas?

Thanks, Matthew

Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge createinstance not firing
  Rick Strahl
  Matthew Olson
  Jun 11, 2020 @ 12:45pm

Is that a typo in the assembly name? The class name? Check the names closely...

IAC, check error messages after each loBridge call with loBridge.cErrorMsg and LoadLibrary() returns true or false depending on whether it worked.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge createinstance not firing
  Matthew Olson
  Rick Strahl
  Jun 11, 2020 @ 02:15pm

Yes, a typo. Grr. TwilioCSharp.dll is the correct name. Now onto my next problem (can't find a dependency), but I'll see if I can't figure that one out on my own. Thanks for your help in solving this one!

Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge createinstance not firing
  Rick Strahl
  Matthew Olson
  Jun 11, 2020 @ 03:37pm

Here's how you figure dependencies out:

  • Look in the bin/Release folder - all DLL files there are dependencies
  • Make sure you copy all the dependencies into your application folder where FoxPro FULLPATH can find them

Better yet if you're building this specifically for your application, set the build output folder in Visual Studio to point at your FoxPro application folder. Then the compiled assembly plus all the dependencies get copied right into your application folder.

+++ Rick --

Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge createinstance not firing
  Matthew Olson
  Rick Strahl
  Jun 12, 2020 @ 07:23am

Yeah, you would think that would be the case, but unfortunately I used the Package Manager Console and it didn't bring in those dependencies for some reason even though that is the recommended way to get them according to Twilio. I had to get nuget.exe and use it to download the Twilio dependencies. Would have been nice if they had documented this somewhere (or if they did, I didn't find it). Oh well, I got what I was missing and have been able to send a test message from the VFP command window. Modifying my code to use in my application now. I should be able to deploy soon! Wish me luck.

Thanks again for all of your help. Looking back on this process, I think I've learned a lot. Typos aside, the way wwDotNetBridge can utilize outside DLLs is quite simple as long as I know what I'm doing (which hopefully I now do).

Thanks, Matthew

Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge createinstance not firing
  Rick Strahl
  Matthew Olson
  Jun 12, 2020 @ 12:45pm

Not sure what you mean. If you add with Nuget to add dependencies the compiled output folder will still have all the dependencies required to run the application. The Dependencies and References lists too will show all the dependencies. Most of those are going to be GAC references but a few are likely to be Twilio related assemblies.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge createinstance not firing
  David Phillips
  Matthew Olson
  Jun 23, 2020 @ 11:22pm

Dear Rick, Sorry about the previous message. The code looked good but not when posted. Trying again to make it look okay.

In the code below I have been using the documentation to check what is available under "loErxDevSdk."

When I use "CreateInstance" intellisense does not expose the subroutines available but if I use "CreateObject" it does. Is this expected behavior?

Could I use CreateObject instead CreateInstance?

It seems necessary to keep creating an instance "loErxDevSdk = loBridge.CreateInstance("ScriptProElectronicRx.DevelopmentSdk") when I need to use "loErxDevSdk." later in my code. Is there a problem with continually running this line of code?

Many thanks for your help, David

SET PROCEDURE TO eRx\wwDotnetBridge ADDITIVE

SET DEFAULT TO E:\F\SPWin\Erx
do wwDotNetBridge && Load wwDotNetBridge library
LOCAL loBridge as wwDotNetBridge
loBridge = CreateObject("wwDotNetBridge","V4")

loBridge.LoadAssembly("ScriptProElectronicRx.dll")

loErxDevSdk = loBridge.CreateInstance("ScriptProElectronicRx.DevelopmentSdk")	&& Load Library

senderId = "595D2"		&&	eRX Phcy ID
receiverId = "00000"	&&	eRx GatewaY id
loErxDevSdk.InitDevelopmentSdk(senderId, receiverId)		&& Set Send/Rec IDs

initSCID = "1K19H1MR3JGT39R7Q8"

*Amend Item

result = loErxDevSdk.RetrieveRxView(initSCID)
MESSAGEBOX(result)
resultTwo = loErxDevSdk.AmendItem()
MESSAGEBOX(resultTwo)
Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge createinstance not firing
  Rick Strahl
  David Phillips
  Jun 24, 2020 @ 12:36am

Whether you use CreateObject() or CreateInstance() both return COM objects and so Intellisense will not work on them unless you explicitly specify the COM interface and if the .NET object is actually registered with COM. Note that it doesn't have to be with wwDotnetBridge and CreateInstance(), but it does with CreateObject().

FoxPro's IntelliSense for COM objects is terrible - if you don't provide the explicit LOCAL type declaration like this and the object is registered as a COM object using that COM ProgId:

LOCAL loItem as SomeNamespace.SomeType 
loItem = loBridge.CreateInstance("SomeNamespace.SomeType")

Intellisense only works with properties you've already referenced. Best way to find out about what's available on various objects is to use a .NET Object browser like Reflector which is included with both Web Connection and wwDotnetBridge (look in the docs for wwDotnetBridge).

It seems necessary to keep creating an instance "loErxDevSdk = loBridge.CreateInstance("ScriptProElectronicRx.DevelopmentSdk") when I need to use "loErxDevSdk." later in my code. Is there a problem with continually running this line of code?

I'm not sure what that means. A .NET COM object variable is like any other variable in FoxPro and has to be in scope as needed. Other than that you create an instance when you need it and obviously if you need 10 instances you can create 10 instances 😃

+++ Rick ---

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge createinstance not firing
  David Phillips
  Rick Strahl
  Jun 24, 2020 @ 05:36am

Thank you Rick, you have given me something to go on with.

Kind regards, David

© 1996-2020