Hi James,
A good place to start would be: http://www.xojo.com/blog/en/2013/12/build-automation.php
Just another vote for possibly the best forum thread of the year;) These methods of creating .NET dllās have been very handy of late.
I do have a problem with deployment though. My newly created dll works fine on my development machine with āRegister for com interopā selected but on a client pc Iām having a lot of problems.
Even using REGASM, Iām finding that my dll isnāt working. It has some dependant dllās and Iām sure this is the case, but to date, without installing visual studio on the clients machine Iām having problems registering. Is there a simple way to determine what isnāt being found as far as dependencies are concerned?
Cheers,
James
Thanks Jim,
Iāve tried DW, but didnāt have much success. Iāll revisit again!
J
James,
Just a thought, if your deployment target is XP, the .net framework itself may need to be installed.
Hey Jim,
Yeah, Iām lucky enough that everything is Win7 now;)
Following on kind of on topic:)
I gave up for now on the deployment of a Com Object (ActiveX) at around 2am. At 3am I had a fully working system using Robert Gieseckeās unmanaged exports (Thanks Gary!).
I did have a few problems with the classes having to be set as static but I got around that by having the externally visible classes set to static and then having these call my complex multi class internal routines. Happy to post code if anyone stumbles across this thread in the future and requires it. Just let me know.
So onto question 2:
Iām using soft declares to connect to my shiny new .dll. They work fine, but are not being released on app quit. If I try to build or run again, the app will not load unless I log out and back in again. Is there a way to force release the dll on app quit?
Question 3 is another curly one relating to thread safety. Iām hoping to call this dll from a few places in my program, but Iām finding that it isnāt thread safe and Iām getting unintended stuff happening. Do I need to make my dll thread safe of is there a way in Xojo to protect it? If I run two apps at the same time accessing the dll it works fine.
Thanks again guys. Hopefully this tread is adding some value to the body of knowledge. I know itās been a steep learning curve for me!
James,
Threading āin processā is not easy outside of the safe threading model that Xojo provides. If you wish to spawn threads inside a DLL , you must terminate those threads gracefully when your Xojo application terminates otherwise you end up with all sorts of chaos and odd behavior.
Without seeing your source code, I canāt tell you exactly what the issue is but having done this myself, on multiple projects, my gut feeling is that you need to look at how you are dealing with this.
Regards,
Jim
Hey Jim,
I guess to answer your question, Iām not dealing with it;) Iām running into the problem of not threading (I guess). Iām starting to think that I need to multithread however Iām not sure that my dll requires it or maybe Iām just writing dodgy code!
The biggest problem I have is that when I quit my app it isnāt releasing the dll. Ie, I canāt rename, move or reopen my program because the dll is in use!
Any suggestions?
I should point out that is only seems to happen in the debugger.
For those of you playing at home, Iāve attached my C# dll in the hope that it may assist another newbie as I was/am in the C# syntax. Iām new to C# (a few days old now;) )
You can see Iām using a static class called using the DLL export command (Connect_Camera). This class creates a new instance of my dynamic classes. Hope this makes sense but at the end of the day my dll connects to a specific IP camera.
Iām connecting to this dll using a standard soft declare and sending a window handle from my Xojo app that enables me to view live footage within Xojo. Kinda neat!
The only remaining issue I have is that when I call this dll twice from the same application, I get all sorts of issues. Mainly pictures switching between multiple cameras etc, hence my question on how to thread. In the short term Iām thinking of compiling multiple dllās with different names in the hope that this will workā¦
Any suggestions on my dodgy code appreciated;)
Cheers,
James
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RGiesecke.DllExport;
using System.Runtime.InteropServices;
using NETSDKDLL_DOTNET;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Forms;
public class TestExport
{
public int nLoginId;
public int nPortId;
public int lRealStreamId;
private fRealDataCallBack fReadlCallBack;
public bool haveLogin;
private fDecCallBackFunction fDecodecall;
public string datain;
public int junk;
public string pXml;
public int Saved_Login;
[DllExport]
public static string Connect_Camera(string IP_addr, short Port_in, string User_in, string Pass_In, int window_handle, int channel, int flip)
{
int temp1;
TestExport TestExport = new TestExport();
temp1 = TestExport.Login(IP_addr, Port_in, User_in, Pass_In);
System.Threading.Thread.Sleep(300); // not sure what i can get away with here
TestExport.Play(window_handle, 1);
return "Win!";
}
public int Login(string ip_addr, short port_in, string user_in, string pass_in)
{
try
{
nLoginId = 5; //this may fix issue of memory leak by killing the connection each time through a call make non zero
// MessageBox.Show("callback yes");
//CString strXML;
PLAYERDLL.IP_TPS_Init();
NETSDKDLL.IP_NET_DVR_Init();
this.fDecodecall = new fDecCallBackFunction(this.OnDecCallBackFunction);
this.fReadlCallBack = new fRealDataCallBack(this.OnRealDataCallBack);
NETSDKDLL.IP_NET_DVR_SetStatusEventCallBack(new StatusEventCallBack(this.OnStatusEventCallBack), IntPtr.Zero);
// MessageBox.Show("here");
if (this.nLoginId != 0)
{
NETSDKDLL.IP_NET_DVR_Logout(this.nLoginId);
this.haveLogin = false;
}
IP_NET_DVR_DEVICEINFO lpDeviceInfo = new IP_NET_DVR_DEVICEINFO();
this.nLoginId = NETSDKDLL.IP_NET_DVR_Login(ip_addr, port_in, user_in, pass_in, ref lpDeviceInfo);
Saved_Login = this.nLoginId;
}
catch
{
MessageBox.Show("error in login");
}
return nLoginId;
}
public int Play(int hwnd, int channel_in)
{
try
{
// nLoginId = loginID;
lRealStreamId = 5; //set these to non zero to try to kill existing streams before connecting a new one may need to be managed
nPortId = 5;
if (this.nLoginId == 0)
{
MessageBox.Show("please login to the device first!");
}
else if (!this.haveLogin) // might need to manage this myself
{
MessageBox.Show("device online state error,please play video after login device!");
}
else
{
if (this.lRealStreamId != 0)
{
NETSDKDLL.IP_NET_DVR_StopRealPlay(this.lRealStreamId);
}
if (this.nPortId != 0)
{
PLAYERDLL.IP_TPS_CloseStream(this.nPortId);
}
IP_NET_DVR_CLIENTINFO lpClientInfo = new IP_NET_DVR_CLIENTINFO();
USRE_VIDEOINFO pUser = new USRE_VIDEOINFO();
lpClientInfo.lChannel = 1;
pUser.nVideoPort = 0x22a;
pUser.nVideoChannle = channel_in;
int handle = hwnd;
pUser.pUserData = (IntPtr)handle;//this.pictureBox1.Handle;
this.nPortId = 1;
this.lRealStreamId = NETSDKDLL.IP_NET_DVR_RealPlay(this.nLoginId, ref lpClientInfo, this.fReadlCallBack, ref pUser, 1);
if (this.lRealStreamId == 0)
{
MessageBox.Show("please login to the device first!");
}
}
}
catch
{ MessageBox.Show("error in play"); }
return lRealStreamId;
}
public int OnDecCallBackFunction(long nPort, IntPtr pBuf, long nSize, ref FRAME_INFO pFrameInfo, IntPtr pUser, long nReserved2)
{
// MessageBox.Show("callback 1");
return 0;
}
public int OnRealDataCallBack(int lRealHandle, int dwDataType, IntPtr pBuffer, int dwBufSize, ref FRAME_EXTDATA pExtData)
{
// MessageBox.Show("callback 3");
IntPtr pUserData = pExtData.pUserData;
if (dwDataType == 0)
{
return PLAYERDLL.IP_TPS_InputVideoData(1, pBuffer, dwBufSize, pExtData.bIsKey, (int)pExtData.timestamp);
}
if ((dwDataType != 1) && (dwDataType == 2))
{
STREAM_AV_PARAM stream_av_param = new STREAM_AV_PARAM();
stream_av_param = (STREAM_AV_PARAM)Marshal.PtrToStructure(pBuffer, stream_av_param.GetType());
int pSize = Marshal.SizeOf(typeof(VIDEO_PARAM));
IntPtr ptr = Marshal.AllocHGlobal(0x2800);
Marshal.StructureToPtr(stream_av_param.videoParam, ptr, false);
PLAYERDLL.IP_TPS_OpenStream(1, ptr, pSize, 0, 40);
Marshal.FreeHGlobal(ptr);
short bHaveAudio = stream_av_param.bHaveAudio;
PLAYERDLL.IP_TPS_Play(1, pUserData);
}
return 0;
}
public int OnStatusEventCallBack(int lUser, int nStateCode, IntPtr pResponse, IntPtr pUser)
{
// MessageBox.Show("callback 2");
// Debug.WriteLine("test");
if (nStateCode == 4)
{
this.haveLogin = true;
}
else if ((nStateCode == 5) || (nStateCode == 0x1f))
{
this.haveLogin = false;
}
return 0;
}
}
[
EDIT : fixed code closing tag
Going to be using this method as we move a large project from RB to .net. As we will be doing it in stages, needed a way to mix new code & windows developed in .net with old code in the RB project this was the perfect find. The example was done in C# so I thought I would include the vb.net equivalent:
[code]Imports System
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
<InterfaceType(ComInterfaceType.InterfaceIsDual)>
Public Interface XojoManagedInterface
<DispId(1)>
Function SayHi(Name As String) As Integer
Sub HelloXojo()
End Interface
<ClassInterface(ClassInterfaceType.None)>
Public Class JordansXojoCode : Implements XojoManagedInterface
Public Function SayHi(Name As String) As Integer Implements XojoManagedInterface.SayHi
MsgBox("hello " + Name)
Return 0
End Function
Public Sub HelloXojo() Implements XojoManagedInterface.HelloXojo
MsgBox("Hello xojo from VB.NET!")
End Sub
End Class[/code]
Going to be using this method as we move a large project from RB to .net. As we will be doing it in stages, needed a way to mix new code & windows developed in .net with old code in the RB project this was the perfect find. The example was done in C# so I thought I would include the vb.net equivalent:
[code]Imports System
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
<InterfaceType(ComInterfaceType.InterfaceIsDual)>
Public Interface XojoManagedInterface
<DispId(1)>
Function SayHi(Name As String) As Integer
Sub HelloXojo()
End Interface
<ClassInterface(ClassInterfaceType.None)>
Public Class JordansXojoCode : Implements XojoManagedInterface
Public Function SayHi(Name As String) As Integer Implements XojoManagedInterface.SayHi
MsgBox("hello " + Name)
Return 0
End Function
Public Sub HelloXojo() Implements XojoManagedInterface.HelloXojo
MsgBox("Hello xojo from VB.NET!")
End Sub
End Class[/code]
[quote=49657:@Jim Cramer]Ohh yes! Iāve used this technique many times in the past to get access to .NET goodiesā¦
There is another way to do this that I havenāt explored yet:
https://www.nuget.org/packages/UnmanagedExports
Essentially this turns any .NET DLL into something we can call with standard XOJO decalresā¦looks interesting.
Regards,
Jim[/quote]
This made my year. Thank you!
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:
[code] 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);
}[/code]
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
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.
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?
[quote=470934:@Alessandro Mandelli]
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?[/quote]
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.
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:
[code]#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[/code]
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:
Also why content is a 24bytes-array, as the return value from the dll is 8 bytes.
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