FoxPro and .NET Interop
wwDotNetBridge Help
Gravatar is a globally recognized avatar based on your email address. wwDotNetBridge Help
  Cesar
  All
  May 8, 2019 @ 08:42pm

Hello, this is my first attempt to work with wwDotNetBridge, and I'm having some difficulties in converting some code that needs a Callback.

Here's the original relevant piece of code:

// NET ORIGINAL CODE
	         _ms = new MemoryStream(emf);
	         _oEMF = new Metafile(_ms);
	         _oBmp = new Bitmap(1, 1);
	         _oGfx = Graphics.FromImage(_oBmp);
	         m_delegate = new Graphics.EnumerateMetafileProc(MetafileCallback);
	         _oGfx.EnumerateMetafile(_oEMF, new Point(0, 0), m_delegate)

My issues are in the implementation of these 2 lines:

	         m_delegate = new Graphics.EnumerateMetafileProc(MetafileCallback);
	         _oGfx.EnumerateMetafile(_oEMF, new Point(0, 0), m_delegate)

... and my converted code

SET PROCEDURE TO wwUtils ADDITIVE
DO wwDotNetBridge
LOCAL loBridge as wwDotNetBridge
loBridge = GetwwDotnetBridge("V4")

? loBridge.LoadAssembly("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")

*!*	         _ms = new MemoryStream(emf)
*!*	         _mf = new Metafile(_ms)
lcFileName = GETPICT("EMF")
loEMF = loBridge.Createinstance("System.Drawing.Imaging.Metafile",lcFileName)

lnWidth  =  850
lnHeight = 1200
loBmp = loBridge.Createinstance("System.Drawing.Bitmap", CAST(lnWidth as Integer), CAST(lnHeight as Integer))

*!*	         _oGfx = Graphics.FromImage(_oBmp)
loGfx = loBridge.Invokestaticmethod("System.Drawing.Graphics","FromImage",loBmp)

*!*	         m_delegate = new Graphics.EnumerateMetafileProc(MetafileCallback)
*!* o.InvokeMethodAsync(loCallbackEvents,loInstance,lcMethod,lvParm1-10)
PUBLIC loMetaFileCallback
loMetaFileCallback = CREATEOBJECT("MyCallbacks")
loEnumProc = loBridge.CreateInstance("System.Drawing.Graphics.EnumerateMetafileProc")
* https://docs.microsoft.com/en-us/dotnet/api/system.drawing.graphics.enumeratemetafileproc?view=netframework-4.8
* callback
* Graphics.EnumerateMetafileProc delegate that specifies the method to which the metafile records are sent.
m_Delegate = loBridge.InvokeMethodAsync(loMetaFileCallback,loEnumProc,"EnumerateMetafileProc") && Parameter missing here???

*!*	         _oGfx.EnumerateMetafile(_oEMF, new Point(0, 0), m_delegate)
loBridge.InvokeMethod(loGfx,"EnumerateMetafile", loEMF, CAST(0 AS INTEGER), CAST(0 AS INTEGER), m_Delegate)
? loBridge.cErRORMSG


*!*	loBrush = loBridge.GetStaticproperty("System.Drawing.Brushes","Black")

*!*	loBridge.InvokeMethod(loGraphics,"FillRectangle",;
*!*							  loBrush,CAST(0 as Integer),CAST(0 as Integer),;
*!*	                          CAST(lnWidth as Integer),CAST(lnHeight as Integer))

*loBridge.InvokeMethod(loGraphics,"DrawImage",;
                      loEMF,CAST(0 as Integer),CAST(0 as Integer),;
                      CAST(lnWidth as Integer),CAST(lnHeight as integer))

loBmp.Save(FULLPATH("TEST1.PNG"))
loBmp.Dispose()

*** Show the image in Windows Viewer
GoUrl(FULLPATH("TEST1.PNG"))
RETURN 

*** Implement a subclass of AsyncCallbackEvents
*** Implement the OnCompleted() and OnError() methods
*** These methods are called on completion
*** Minimize state changes here and make them quick
*** the code in these method fires in the middle of
*** other FoxPro code.
DEFINE CLASS MyCallbacks as AsyncCallbackEvents

FUNCTION OnCompleted(lvResult,lcMethod)
SET STEP ON 

? "Success: " + lcMethod,lvResult
RETURN 


*!*	*// Define callback method.
*!*	    private bool MetafileCallback( EmfPlusRecordType recordType, int flags, 
*!*	                                                 int dataSize, IntPtr data,
*!*	                                                 PlayRecordCallback callbackData)
*!*	    {
*!*	      LB.Items.Add(recordType);
*!*	      if ( dataSize > 0 )
*!*	      {
*!*	        byte[] D = new byte[dataSize];
*!*	        Marshal.Copy(data, D, 0, dataSize);
*!*	        mf.PlayRecord(recordType, flags, dataSize, D);
*!*	      }
*!*	      return true;
*!*	    }


ENDFUNC

FUNCTION OnError(lcMessage,loException,lcMethod)
	? "Error: " + lcMethod,lcMessage
ENDFUNC

ENDDEFINE

Thanks in advance for any insights that you can provide! Cesar

Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge Help
  Rick Strahl
  Cesar
  May 9, 2019 @ 12:37am

Hi Cesar,

hmmm... I think that one is not possible because I don't believe you can map a delegate to a FoxPro method. wwDotnetBridge supports event handling but not direct delegate invocation or at least I don't know off hand how we could map the delegate to a FoxPro function - the delegate has to point back to something inside of .NET and that has to forward the call to FoxPro.

The way I would typically handle this is with a stub routine inside of .NET with an assembly you create. You can handle the callbacks inside of that bit of .NET code and then use a passed in FoxPro object to get the callbacks to fire inside of FoxPro.

There's probably a way to make wwDotnetBridge support delegates in the same way events are handled (which are essentiall delegates), but it requires an intermediary in .NET to actually capture the delegate invocation. However there are so few APIs left that use delegates directly rather than exposing events or - these days - using Task, Action or Func refs that I'm not inclined to add support for this at this point.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge Help
  Cesar
  re: wwDotNetBridge Help
  May 9, 2019 @ 04:33am

Hi Rick, Thanks a lot for the quick answer. I've decided to create a DLL in C# for that case. BTW, I'm really deeply impressed with your job with wwDotNetBridge. Extremely helpful. Best regards, Cesar

Gravatar is a globally recognized avatar based on your email address. re: wwDotNetBridge Help
  Rick Strahl
  Cesar
  May 9, 2019 @ 01:40pm

Hi Cesar,

I've decided to create a DLL in C# for that case.

Yes, I think that's a good move. My personal rule is that if I have to write more than 10 lines of code in Fox to access a .NET Component or if I have to resort a lot to use ComValue to force types it's better (and more performant) to use a wrapper method in .NET. Once you decide to have a wrapper it's then easy to add other things and call that in the DLL as well for easier integration and usually better performance.

It's the first one where you usually want to avoid the extra dependency, but in almost all cases I think it's worth it especially since the actual DLL are usually tiny.

BTW, I'm really deeply impressed with your job with wwDotNetBridge. Extremely helpful. Best regards, Cesar

Thank you - appreciate that.

A lot of experience from feedback is buried in that library - the basics of what it does are super simple, but all the details around edge cases and type conversion are tricky and I hope the library makes a lot of that much easier (and some things that otherwise wouldn't be possible).

+++ Rick ---

© 1996-2019