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
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 ---
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
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 ---
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!
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 --
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
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 ---
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)
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 ---