Using C# classes in Xojo

  1. ‹ Older
  2. 3 weeks ago

    Sorry if I revive a two years old thread. I'm in the process of trying to convert an existing c# .net dll library for reuse into XOJO. I'm new to this and I'm trying to inspect the possibility to migrate my codebase from vb/c# to XOJO.
    I'm a newbie, so please be gentle with me.

    I've got a class which contains some methods but also some constructors, for examble:

            public PortClient(string serialPort)
            {
                this.serialport = new SerialPort();
                serialport.PortName = serialPort;
                serialport.BaudRate = baudRate;
                serialport.Parity = parity;
                serialport.StopBits = stopBits;
                serialport.WriteTimeout = 10000;
                serialport.ReadTimeout = connectTimeout;
               
                serialport.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);      
            }

    I can't seem to be able to export the DLL to with UnmanagedExports as the directive [DllExport] can only be used for methods, but not for constructors. I know that I would probably need to contact Giesecke, the author of UnmanagedExports, but I also thought somebody already solved this in a clever way I didn't.

    Thanks

  3. Rick A

    Jan 10 Pre-Release Testers (Brazil. UTC-3:00)
    Edited 3 weeks ago

    This technique is not intended to obtain and handle DotNet objects at Xojo side, they're not compatible. Anything related to a DotNet object must be done at DotNet side, and can be handled by exported static methods. So, in theory, you can create and store your C# objects in some DotNet static property, like an array of objects if you need more than one, or just a property object of such type, using some static Factory Function like "public static int CreateMyPortClient(string serialPort)" that will create a new SerialPort(), set everything, store it in an array of SeriaPort()'s, give its int index back to be used as a handle in other static calls at Xojo side, like, e.g. ChangeBaudRate(handleIndex, newBaudRate), something like this. The entire "engine" you will have to take care of. I hope you understand what I mean.

  4. You are telling me that I can't reference a dll and consume it for creating objects at runtime?
    it would be better to rewrite the whole library class in xojo?

  5. Rick A

    Jan 10 Pre-Release Testers (Brazil. UTC-3:00)

    @AlessandroMandelli
    You are telling me that I can't reference a dll and consume it for creating objects at runtime?
    it would be better to rewrite the whole library class in xojo?

    Well, I said one way of doing it. Maybe someone can have another solution that I don't know.

    Rewriting in Xojo is one option, but anything is up to you.

  6. 2 weeks ago
    Edited 2 weeks ago

    I've been able to export the dll with the interfaced class as per first suggestion in this thread.

    using System;
    using System.Net.Sockets;
    using System.Net;
    using System.IO.Ports;
    using System.Reflection;
    using System.Text;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    
    [Guid("f5f505ff-635b-4cb0-8481-726a8353a78f")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface IXojoManagedInterface
    {
        int[] ConvertIntToRegisters(int intValue);
    }
    namespace XojoEasyModbus
    
    {
        /// <summary>
        /// Implements a ModbusClient.
        /// </summary>
        /// 
        [Guid("017b1254-7c54-4bd3-8bc9-d84441791088")]
        [ClassInterface(ClassInterfaceType.None)]
        public partial class XojoModbusClient : IXojoManagedInterface
        {
            /// <summary>
            /// Converts 32 Bit Value to two ModbusRegisters
            /// </summary>
            /// <param name="intValue">Int value which has to be converted into two registers</param>
            /// <returns>Register values</returns>
            public int[] ConvertIntToRegisters(Int32 intValue)
            {
                byte[] doubleBytes = BitConverter.GetBytes(intValue);
                byte[] highRegisterBytes = 
                {
                    doubleBytes[2],
                    doubleBytes[3],
                    0,
                    0
                };
                byte[] lowRegisterBytes = 
                {
                    
                    doubleBytes[0],
                    doubleBytes[1],
                    0,
                    0
                };
                int[] returnValue =
                {
                    BitConverter.ToInt32(lowRegisterBytes,0),
                    BitConverter.ToInt32(highRegisterBytes,0)
                };
                return returnValue;
            }
        }
    }

    This above is just a snippet.

    The function is implemented in the COM Interop as this:

    #if TargetWin32
      If mThis = Nil Then Raise New NilObjectException
      Dim func As New ConvertIntToRegisters_Func2(mThis.Ptr( 0 ).Ptr(22 * COM.SIZEOF_PTR ))
      Dim resultCode As Integer
      Dim Return_pRetVal_Param As  New MemoryBlock( COM.SafeArray.Size )
      resultCode = func.Invoke(mThis, intValue_Param, Return_pRetVal_Param)
      If resultCode = 0 Then
        Dim retVal As COM.SafeArray
        retVal.StringValue(True) = Return_pRetVal_Param.StringValue(0, COM.SafeArray.Size)
        Return retVal
      Else // Throw Exception
        Raise New COM.COMException("Failed on ConvertIntToRegisters", resultCode)
      End If
      
    #endif

    From within Xojo I implemented a method like this:

    dim o as new XojoEasyModbus.XojoModbusClient
    dim temp as COM.SafeArray = o.ConvertIntToRegisters(123)
    dim content() as UInt8 = temp.ByteValue

    Calling the function is performed without problems, the SafeArray contains data, but it's meaningless.
    Not only that, but the content of temp change every time the function is called.
    Like:
    -image-

    Also why content is a 24bytes-array, as the return value from the dll is 8 bytes.

  7. Brock N

    Jan 13 Pre-Release Testers, Xojo Pro
    Edited 2 weeks ago

    With the introduction of .Net Core C# you can build cross platform DLL's. Does anyone know if there is a generic way from Xojo to call and utilize one of these libraries?

    I did find this article:
    https://docs.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com

    But would love to see if someone can throw together a sample Xojo project and .Net library working together

  8. No one?

  9. Julian S

    Jan 16 Pre-Release Testers, Xojo Pro UK
    Edited 2 weeks ago

    Have you checked the values in retVal to make sure they aren't pointers to your values and you're not being sent a com.safearray? 24 bytes would be one 8 byte pointer to each value and a null 8 bytes to signify the end of the array of pointers. Just a guess as I've not tested this personally but it might explain why the values change on every call.

  10. I'm being sent a COM.SafeArray. I'm not sure what's inside the 24 bytes, it's variable garbage to me. It can be anything or nothing at all.

  11. Julian S

    Jan 17 Pre-Release Testers, Xojo Pro UK

    If you zip up a demo vs project and xojo project I'll take a look.

  12. Thanks, I will

  13. last week

    @Julian S If you zip up a demo vs project and xojo project I'll take a look.

    There you are, thanks for your help
    https://1drv.ms/u/s!ApYaAzYkAIgXqzt0xwoWpvtVCuqI?e=T1bZYv

  14. Julian S

    Jan 20 Pre-Release Testers, Xojo Pro UK

    Thanks Alessandro, but that is just three lines from above put in a xojo project.

    Could you make a small simple VS project showing what you're doing and the accompanying small Xojo project, zip them both up and link them?

  15. There is nothing more than that at this point. I'm just evaluating whether it is feasible and working. It's a simple call to a c# dll library. Just build the cs file with the giesecke reference in it and call it from the xojo project.

  16. Julian S

    Jan 20 Pre-Release Testers, Xojo Pro UK

    I was trying to get you to provide me with a working example of a simple VS and Xojo project so I didn't have to read up on getting COM working in Xojo and getting the correct code and options in VS just to get to the point you were already at, never mind :)

    As I originally thought, the value returned from the Invoke is an address of a pointer to a COM.SafeArray so the Delegate needs to have ByRef added and changed to a Ptr

    ConvertIntToRegisters_Func2(this As Ptr, intValue_Param As Integer, ByRef pRetVal_Param As Ptr)

    Then inside ConvertIntToRegisters use the following code

    #if TargetWin32
      If mThis = Nil Then Raise New NilObjectException
      Dim func As New ConvertIntToRegisters_Func2(mThis.Ptr(0).Ptr(7 * COM.SIZEOF_PTR))
      Dim resultCode As Integer
      Dim Return_pRetVal_Param As New MemoryBlock(COM.SafeArray.Size)
      Dim p As Ptr 'added this
      'resultCode = func.Invoke(mThis, intValue_Param, Return_pRetVal_Param) 'removed as we're going via another pointer
      resultCode = func.Invoke(mThis, intValue_Param, p) 'used this version instead
      Return_pRetVal_Param = p
      If resultCode = 0 Then
        Dim retVal As COM.SafeArray
        retVal.StringValue(True) = Return_pRetVal_Param.StringValue(0, COM.SafeArray.Size)
        Return retVal
      Else // Throw Exception
        Raise New COM.COMException("Failed on ConvertIntToRegisters", resultCode)
      End If
      
    #endif

    The calling code then changes to

    Dim o As New ClassLibrary2.XojoModbusClient
    Dim temp As COM.SafeArray = o.ConvertIntToRegisters(123)
    Dim content() As UInt8 = temp.ByteValue
    Dim data As Ptr = temp.pvData

    Where data will contain your returned values.

    This might be a bug with the auto code generation of the Xojo COM importer but as I've not done COM in around 18 years I'm not really in a position to say so with conviction.

  17. I'll give it a try later today or tomorrow. For the time being please accept my greatest thank you for you invaluable help.
    Would you advice to report the bug and if yes, how can I do it?

  18. Edited last week

    Sorry, but it still doesn't work. This is my ConvertIntToRegisters

    #if TargetWin32
      If mThis = Nil Then Raise New NilObjectException
      Dim func As New ConvertIntToRegisters_Func2(mThis.Ptr( 0 ).Ptr(15 * COM.SIZEOF_PTR ))
      Dim resultCode As Integer
      Dim Return_pRetVal_Param As  New MemoryBlock( COM.SafeArray.Size )
      Dim p As Ptr 'added this
      'resultCode = func.Invoke(mThis, intValue_Param, Return_pRetVal_Param) 'removed as we're going via another pointer
      resultCode = func.Invoke(mThis, intValue_Param, p) 'used this version instead
      If resultCode = 0 Then
        Dim retVal As COM.SafeArray
        retVal.StringValue(True) = Return_pRetVal_Param.StringValue(0, COM.SafeArray.Size)
        Return retVal
      Else // Throw Exception
        Raise New COM.COMException("Failed on ConvertIntToRegisters", resultCode)
      End If
      
    #endif

    I've added and removed rows as per your suggestion, but please note the size of Ptr at row 3.
    Changing it to 7 * COM.SIZEOF_PTR as per your example causes the function to crash at row 7 func.invoke.
    Leaving it at 15 * COM.SIZEOF_PTR the execution ends nicely, but the return value is still a 24-items array, this time empty.

    -image-

    I'm at a loss here.

  19. Julian S

    Jan 21 Pre-Release Testers, Xojo Pro UK

    Ah yes the 15 will need to remain the same as your dll has more calls than mine.

    You missed this line, which puts the data into the memoryblock

    Return_pRetVal_Param = p

    place it on the line before the If resultCode = 0 Then

    I'll see about adding the feedback ticket when I get a moment.

  20. 6 days ago
    Edited 6 days ago

    Thanks a lot, it now works as supposed, though much more work is necessary... your help has been invaluable and I will forever be in debt. I will stop here for the time being, as I have to import a number of conversion routines, for which I can't manually edit the interop function...I will wait for the ActiveX import to be debugged.
    In the example above I used the following:

    Dim o As New XojoEasyModbus.XojoModbusClient
    Dim temp As COM.SafeArray = o.ConvertIntToRegisters(123123)
    Dim content As UInt32 = temp.pvData.UInt32
    Dim register(1) as UInt16
    register(0) = content AND &H0000FFFF
    register(1) = BitWise.ShiftRight(content AND &HFFFF0000, 16, 32)

    I realise it makes very little sense to split a 32bit integer into bytes, encoded into a 2x16bit array, get it back as 32bit consecutive bits with 4bytes memory pointer, and then mask and shift the 32bits, 16bits at a time.
    This was sort of an exercise for testing Xojo capabilities.
    i probably found a bug into UTF32 encoding, for which a separate thread will be opened.

or Sign Up to reply!