I'm running into this old chestnut again and the prior documented solutions are not solving the problem. Using v6.1.0 of WW Client Tools
I've verified that the DLL is not blocked. I've verified that both V2 & V4 .Net frameworks are installed. I've recompiled all the WW prgs. I've tried running VFP as an admin. No joy.
IF EMPTY(m.tcVersion)
m.tcVersion=[V2]
ENDIF
ASSERT .f. MESSAGE [test dotnetbridgetester?]
do wwDotNetBridge
LOCAL loBridge as wwDotNetBridge
loBridge = CreateObject("wwDotNetBridge",m.tcVersion)
? loBridge.GetDotnetVersion()
? loBridge.cErrorMsg
The load of wwdotnetbridge fails on this line:
lnDispHandle = ClrCreateInstanceFrom(FULLPATH("wwDotNetBridge.dll"),;
"Westwind.WebConnection.wwDotNetBridge",@lcError,@lnSize)
and returns this error:
Unable to load Clr Instance. 0x8013101b:
Deep thoughts, anyone?
tia
Make sure you're loading the version of the framework that's installed on your machine. Most likely V4 and make sure that this is a full framework version and not the short lived hobbled .NET 40 Client Framework.
Specifically load V4. it looks like your code is trying to load 2.0 and it may not be on your machine.
loBridge = CreateObject("wwDotNetBridge","V4")
+++ Rick ---
Thanks for the followup, Rick. For brevity's sake I only showed the one test but I did try this with both V2 & V4 and it consistently failed for both. I was even restarting my dev environment each time when testing because I had seen the commentary about only one instance at a time in a process. Of course, I just tried again and this time it worked. You must remember to only use your powers for good....
Yup you definitely need to restart in between since only one runtime can load. OTOH, if it fails the runtime never loaded so in that case you should be able to load with the other type.
Rule of thumb: Always be explicit about your wwDotnetBridge() runtime selection. Put a call to load wwDotnetBridge into startup of your application to ensure that you are loading the version you're expecting. All subsequent loads will always use the version that was previously loaded regardless of what you specify in the version number.
+++ Rick ---
At the moment, I don't load the object at application startup; only when I call for stuff that I use like FTP and SMTP. Having said that, I will give it some more thought. This whole thread was triggered by a client reporting problems with sending email on certain machines.
Thanks again for the followup.
It looks like wwsmtp.prg, which I call directly in my code, is not setup to pass a version request to the method that creates the bridge object. I can, of course, edit my own copy but thought I should pass this on.
No, please don't do that, Richard - it's a bad idea.
You should EXPLICITLY call wwdotnetbridge
in your startup code to force the version in the application.
*** Just Do this in your app's startup code to force the .NET Version
loBridge = CREATEOBJECT("wwDotnetBridge","4.0")
This will force the rest of your app to use .NET 4.0 no matter what you pass into the version designator.
If you don't do this, and instead let the first version of a library that you hit determine the version you'll never know exactly what you might get. If you call .NET 2.0 component first then you get that. If you call .NET 4.x component first then you get that.
So - always explicitly force the version in your startup.
I don't want to explicitly force versions in libraries that can run on multiple platforms as wwSmtp can. The right way to to do this is have the application decide the version, not the library you are using 😃
Maybe I should add an explicit UDF() function called SetDotNetVersion() to make it real obvious...
+++ Rick ---
I added this function now to wwDotnetBridge():
It doesn't really do anything new or special, just adds a new name that is way more explicit:
************************************************************************
* InitializeDotnetVersion()
****************************************
*** Function: Explicit method that simply creates a wwDotnetBridge
*** instance. Call this method at the beginning of your
*** app to force the .NET Runtime version that will be
*** used throughout the entire application.
*** Assume: Call this method in your application startup
*** Pass: "V2" or "V4"
*** Return: wwDotnetBridge instance
*** (you don't have to do anything with it)
************************************************************************
FUNCTION InitializeDotnetVersion(lcVersion)
RETURN GetwwDotnetBridge(lcVersion)
ENDFUNC
+++ Rick ---
I get your point but am still a little unclear on scoping so let me ask another question. Are you saying that if I simply instantiate a local bridge object on startup, the .Net version will be set for the lifetime of the application object even if that bridge object goes away?
I get your point but am still a little unclear on scoping so let me ask another question. Are you saying that if I simply instantiate a local bridge object on startup, the .Net version will be set for the lifetime of the application object even if that bridge object goes away?
Yes.
This isn't a matter of scope - you don't have to 'attach' the wwDotnetBridge object to anything or store a reference. You just create it, it creates the runtime and that runtime stays loaded and can never be unloaded until you shutdown the app.
+++ Rick ---
That's what I thought, Rick. Thanks for the clarification.
BTW I found that passing V4 or V2 as the version parameter seemed to do the job whereas using 4.0 or 2.0 just falls through the case logic.
IF !EMPTY(lcVersion)
LOCAL lcShortVer
lcShortVer = UPPER(lcVersion)
DO CASE
CASE lcShortVer == "V4"
lcVersion = "v4.0.30319"
CASE lcShortVer == "V2"
lcVersion = "v2.0.50727"
ENDCASE
this.cClrVersion = lcVersion
ENDIF
this.lUseCom = llUseCom
You much change the line
lcShortVer = UPPER(lcVersion)
into
lcShortVer = LEFT(UPPER(lcVersion),2)
That code snippet is from the current version of wwDotNetBridge.prg. I think I'll leave tweaking that to Rick. 😃
Of course you can, if you want.
That's because 2.0
and 4.0
are not valid version numbers.
Valid version strings are:
- V4
- V2
- v4.0.30319
- v2.0.50727
The latter two are the only true runtime that still matter, which is why you really only should be using V4
or V2
since those are merely aliases for the other two.
The version number can be any specific .NET version number using the official .NET version syntax (ie. like those last two entries) if there should be a newer version, which I don't think will happen any time in the medium term future. Microsoft is just rev'ing the 4.x runtime versions in place - the V4 version numbers hasn't rev'd since it was first released many years ago.
In short for all intents and purposes the only runtime versions in use today are served by the V4
and V2
monikers and that's what you should be using and only once in your application at startup preferrably to force the runtime for the app instance.
+++ Rick ---
Right. I was only mentioning it because your sample code for loading the framework used "4.0" instead of "V4"; a foolish consistency being the hobgoblin of little minds like mine... 😃