Results 1 to 27 of 27

Thread: Working with an external 32bits dll in a x64 environment

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Working with an external 32bits dll in a x64 environment

    Hi everyone,

    I came across some problems recently with two forms that were used independantly and that absolutely need to co-exist (either linked or copied into only one form).

    Here is the situation:

    - The first form was used to control micrometric translators and uses an external dll named XPS_Q8_drivers.dll. The functions from this dll are loaded using:

    Code:
    Declare Function [Func Name] Lib "XPS_Q8_drivers.dll" (ByVal..., ...)  As Integer
    Everything works fine in a standalone app that uses the x86 option when compiling.

    - The second form is an app for image processing. It has a reference to an external dll from the Matrox company called Matrox Imaging Library (MIL). It is referenced in the project and imported using:

    Code:
    Imports Matrox.matroximaginglibrary
    This form raises an exception if compiled with the x86 option. The library is installed on a x64 win 7 environment and installed itself with the x64 package (the 32bits Matrox package cannot be installed on a x64 win7 PC, I've checked this). The exception is of the BadImageFormat type. When the x64 or the AnyCPU option is chosen, the program runs fine and all the function of MIL are functionnal.

    Problem:
    Now I have to combine the two as the movement of translation (of the first form) should be dependant on image processing results (presently calculated with the 2nd form). As I tried to put the code of the 1st form in the second I realized the first .dll was only compatible with the x86 option for compilation.

    What would be the best way to link these two ? I've already worked some hours on this and I've barely found anything to progress...

    Thanks !

  2. #2

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    I forgot to mention that I'm using VB.NET with Visual Studio 2010 Express / .NET Framework 4

  3. #3

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    I've asked for a x64 version of the "XPS_Q8_driver.dll" obviously. I'm waiting for an answer from the developer team at Newport Corp.

    If there is no x64 version, what would be a path to use both of them, even if I have to call another form or sthg like that ?

  4. #4
    PowerPoster Jenner's Avatar
    Join Date
    Jan 2008
    Location
    Mentor, OH
    Posts
    3,712

    Re: Working with an external 32bits dll in a x64 environment

    This might be a situation where you need to write two programs. Create a small, x86 console application that calls and uses the XPS_Q8_driver.dll. Have your main program launch it as needed via Process.Start() and pass data back and forth to it.
    My CodeBank Submissions: TETRIS using VB.NET2010 and XNA4.0, Strong Encryption Class, Hardware ID Information Class, Generic .NET Data Provider Class, Lambda Function Example, Lat/Long to UTM Conversion Class, Audio Class using BASS.DLL

    Remember to RATE the people who helped you and mark your forum RESOLVED when you're done!

    "Two things are infinite: the universe and human stupidity; and I'm not sure about the universe. "
    - Albert Einstein

  5. #5

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    thanks for this input Jenner ! much appreciated.

    Is a x64 compiled form compatible/capable of launching an external x86 app ? It has to be a console app though ?

    I've never used console mode yet (and not used Process.Start() either) what does it imply here to communicate correctly with my windows form ?

    I need to send coordinates to a controler, so the args are almost all integers/double.

    Anyway, thx for the tip again !

  6. #6
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Working with an external 32bits dll in a x64 environment

    I would just use UDP to transfer the data between the two applications.
    A quickly put together example, which will transfer two 32-bit integers from one application to the other. I compiled the sender as a 64-bit app, and the receiver as a x86 (32-bit) app since that is your particular need, but it really doesn't matter. The port number 12321 was chosen arbitrarily.
    The sender application will simply take the mouse X,Y coordinates (if the left button is down) and pack the two integers in a byte array and send it out as a UDP packet on the local loop back IP "127.0.0.1".
    Code:
    Imports System.Net
    Imports System.Net.Sockets
    Public Class Form1
      Dim udpSender As New UdpClient
      Dim destIPnt As New IPEndPoint(IPAddress.Parse("127.0.0.1"), 12321)
    
      Private Sub Form1_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        If e.Button = Windows.Forms.MouseButtons.Left Then
          Dim b(7) As Byte
          Buffer.BlockCopy(BitConverter.GetBytes(e.X), 0, b, 0, 4) 'convert X to 4 byte array and copy to b(0..3)
          Buffer.BlockCopy(BitConverter.GetBytes(e.Y), 0, b, 4, 4) 'convert Y to 4 byte array and copy to b(4..7)
          udpSender.Send(b, b.Length, destIPnt)
        End If
      End Sub
    
      Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        Dim b() As Byte = {0, 0, 0, 0, 0, 0, 0, 0}
        udpSender.Send(b, b.Length, destIPnt)   'To ensure socket is open before the first use in the MouseMove event (otherwise major delay)
        Me.Text = "Drag on this form to draw on the receiving form"
      End Sub
    End Class
    The receiver creates a background thread that will sit waiting for data to come in on that port.
    If there are at least 8 bytes, it will assume it is valid, and convert the bytes into two Integers, and draw a line from the last point it received to the new point on a bitmap, and then invoke the form's Invalidate method on the GUI thread (a non-GUI thread can't access controls on the GUI directly, some method has to be invoked as a delegate so it runs on the GUI thread). The bitmap happens to also be the Form's background image, so the form will refresh the screen from that bitmap when invalidated.
    Code:
    Imports System.Threading
    Imports System.Net
    Imports System.Net.Sockets
    
    Public Class Form1
      Dim drawMap As New Bitmap(1200, 1000)
      Dim gMap As Graphics
    
      Dim receiveThread As New Thread(AddressOf Receiver)
    
      Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        Me.Text = "Receiving form"
        Me.DoubleBuffered = True
        Me.BackgroundImage = drawMap
        gMap = Graphics.FromImage(drawMap)
        receiveThread.IsBackground = True
        receiveThread.Start()
      End Sub
    
      Private Sub Receiver()
        Dim socketIn As New UdpClient(12321)  'Listen on port UDP port 12321
        Dim remoteIP As New IPEndPoint(IPAddress.Any, 0)  'will hold who sent the data (not utilized)
        Dim b() As Byte 'buffer to receive data
        Static LastPoint As Point
    
        Do
          b = socketIn.Receive(remoteIP)
          If b.Count > 7 Then
            Dim newPoint As Point
            newPoint.X = BitConverter.ToInt32(b, 0)  'convert first 4 bytes (0..3) to Int32 
            newPoint.Y = BitConverter.ToInt32(b, 4)  'convert second 4 bytes (4..7) to Int32
            gMap.DrawLine(Pens.Black, LastPoint, newPoint)
            LastPoint = newPoint
            Me.BeginInvoke(Sub() Me.Invalidate()) 'Have to use Invoke to do stuff on the GUI thread
          End If
        Loop
    
      End Sub
    End Class
    That's all there is to this example. Just paste the first code into a new project, and the second code into another project and run them.

    p.s. I originally used Me.Invoke, which suspends the background thread until the Invocation (i.e. the form is updated) is done. This can cause UDP packets to build up in the receive buffer, and the drawing will look like it continues to draw for some time after you stop moving the mouse.
    Using BeginInvoke allows the GUI to do its thing, meanwhile the Background task is returned to immediately and can continue processing in parallel.
    The result is, the background task can process the messages quickly and update the bitmap multiple times in the span of time it takes the form to update the screen once. The application will seem much more responsive since each update of the screen will show the accumulated drawing from several mouse movement events, and you won't see a lag in reaction to mouse movement.
    The thing to be careful about using BeginInvoke, is if two threads try to access a common object (usually a class object) at the same time (i.e. here the bitmap is being used by both the Form to refresh the screen and the background thread to draw the line), you can get an Object Busy exception.
    I haven't seen it happen in this brief test (the line drawing probably takes very little time), but I would probably do a SyncLock on the drawMap object when accessing it in each thread to protect against simultaneous access.
    Last edited by passel; May 16th, 2014 at 01:25 PM. Reason: Change Invoke to BeginInvoke, so can process multiple UDP packets per form refresh

  7. #7

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    OK, that seems like a very useful answer passel !

    First, Thank you very much for your time !

    I should have an answer in a couple of days about the x64 dll from the company designing the controler I'm using. If their answer is negative I'll dig into this as it appears to me it's the only real alternative.

    The problem I foresee is that I need to refer very often to the functions that are in this DLL.

    For example:

    "Init controler" => DLL
    "Create translator group XY" (2 axis) => DLL

    is simple as it's refered to only once at startup.

    But all the move commands are periodic as soon as I need to re-center the camera or focus on a particular field during acquisition. So it's basically:

    "go to 0" => x32 controller DLL
    "camera does sthg" => x64 image processing DLL
    "go to field 1"=> x32 controller DLL
    etc...


    Anyway, thanks for all the research and code posted here, I'm very grateful !

  8. #8
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Working with an external 32bits dll in a x64 environment

    Quote Originally Posted by LiamC View Post
    ...
    I need to send coordinates to a controler, so the args are almost all integers/double.
    ...
    "go to 0" => x32 controller DLL
    "camera does sthg" => x64 image processing DLL
    "go to field 1"=> x32 controller DLL
    etc...
    I'm not sure how the two statements correlate, whether you need to transfer numeric values, i.e. integers/doubles, or you need to transfer string commands, or whether the string portion is relatively standard, i.e. "go to x" or "go to field y", where the "go to" or "go to field" could be represented by a number (which you could do a case statement on) followed by the appropriate parameters as numeric values, or you simply want to treat the whole command as a string with the numbers as text in the string.

    {edit: I looked at the XPS_Q8_drivers.bas file, and I see that it is not a string interface, but typical API method calls, passing parameters to the methods, so the above "examples" are logical descriptions, not specific examples, as I could not find an entry that looks like "go to" in the list}

    I also don't know what speed you need, or the latency overhead.
    {edit: Did get some idea after briefly looking at the XPS-Q8 Controller User's Manual which is where some of the info at the end came from}

    If you run the example code above you should see the drawing on the one form following the mouse movement on the other form with no perceptible lag. My impression based on passed observations is that the MouseMove events occur at a maximum rate of 128hz, so the coordinates are being transmitted between those applications at up to that rate, 128 times per second. If you have a GigaBit network, with GigaBit Ethernet ports I believe you should be able to transfer data between two machines at least 50,000 times per second.
    Since your two applications would be running on the same machine, you don't actually involve the Ethernet ports, so the Bandwidth should be much higher with the internal driver and Ethernet stack protocol delays being the limiting factor, not the Ethernet port speed.
    I usually don't have to deal with pushing the limits of the Network Bandwidth. Most data I deal with is transmitted at 60hz, 50hz, 30hz or on demand.
    There has been the case with streaming sensor data over a TCP connection with larger buffers, say 115000 bytes or so at 30hz between machines, which use a larger chunk of bandwidth, but still I haven't had an issue with VB.Net receiving data at that rate on one TCP connection (as Server) and relaying that data out on another TCP connection (as Client) as part of a sensor simulation.
    It looks like the XPS-Q8 is controlled over a TCP connection and the Ethernet Port is a 10/100 so you're not dealing with GigaBit speeds. Hopefully the dll is just routines used to manage that TCP interface and there are no local hardware drivers involved. It would seem if that is the case, they could just compile a 64-bit version of the dll without a lot of effort.
    Worse case, you have to define a protocol to replace the API calls with a message you transmit to a 32-bit application that does the real API call, and can relay responses back to the 64-bit application. There shouldn't be too much latency given the lower speed the TCP connection is using, but depending on how many API entries you use, and the type of data that has to be returned, it might be a bit of a task.
    Good luck.
    Last edited by passel; May 19th, 2014 at 06:35 AM.

  9. #9

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    Thx a lot passel for your inputs !

    Quote Originally Posted by passel View Post

    {edit: I looked at the XPS_Q8_drivers.bas file, and I see that it is not a string interface, but typical API method calls, passing parameters to the methods, so the above "examples" are logical descriptions, not specific examples, as I could not find an entry that looks like "go to" in the list}
    In the list there are the GroupMoveAbsolute and GroupMoveRelative functions, and then you can retrieve values with GroupPositionCurrentGet for example.

    It's all on a local machine, but the controler is on Ethernet network: so the values retrieved or sent to the controler go through a TCP port indeed.
    The camera is on GigE network on the other RJ45 port of the machine for frame grabbing.

    I haven't got any information yet on the way they are compiling the DLL.

  10. #10

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    Hi,

    @passel: I've tested your code and it really looks good and close to what I would aim for !

    Two integers (4 digits longs xxxx type, coordinates) sent from the main (x64 image processing app) to a background app (x32 motion controler app) so it can start the motion on the microtranslator stages.

    one of the difference I've not put my eyes on yet: I don't really need a form to appear when commands are sent to the x32 app. It would be better to remain invisible. So:

    - How would you launch the x32 motion app without clicking on it if possible ?
    - How would you make the layout form disappear (here with your example one needs to see the drawing so it has a sense, I on the other hand only need to send commands to a controller with the 2 integers I've received) ?

    Thx a million !

    Cheers

  11. #11
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Working with an external 32bits dll in a x64 environment

    Depends on how hidden you need to be. There is probably a way to make a formless application, but I don't feel like researching it.
    You can change a couple of properties on the x32 motion app form in the IDE, so you won't see it (except in the Task Manager process list, but it would still be possible to Alt-Tab to it).
    Set the WindowState property to Minimized.
    And set the ShowInTaskBar property to False.

    You can kick it off from the 64-bit process by using a process object. An example of one way (assumes a copy of the receiver executable file, named in this example UDPReceiver.exe, is moved to the directory with the 64-bit application). Look for "ReceiverProcess" in the code below, to see how it is launched (and killed when the main program exits).
    Code:
    Imports System.Net
    Imports System.Net.Sockets
    Public Class Form1
      Dim udpSender As New UdpClient
      Dim destIPnt As New IPEndPoint(IPAddress.Parse("127.0.0.1"), 12321)
      Dim ReceiverProcess As Process
    
      Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        ReceiverProcess.Kill()    'kill the application associated with ReceiverProcess object
        ReceiverProcess.Close()   'free resources associated with ReceiverProcess object
      End Sub
    
      Private Sub Form1_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        If e.Button = Windows.Forms.MouseButtons.Left Then
          Dim b(7) As Byte
          Buffer.BlockCopy(BitConverter.GetBytes(e.X), 0, b, 0, 4) 'convert X to 4 byte array and copy to b(0..3)
          Buffer.BlockCopy(BitConverter.GetBytes(e.Y), 0, b, 4, 4) 'convert Y to 4 byte array and copy to b(4..7)
          udpSender.Send(b, b.Length, destIPnt)
        End If
      End Sub
    
      Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        Dim b() As Byte = {0, 0, 0, 0, 0, 0, 0, 0}
    
        ReceiverProcess = Process.Start(IO.Path.Combine(Application.StartupPath, "UDPReceiver.exe"))
        udpSender.Send(b, b.Length, destIPnt)   'To ensure socket is open before the first use in the MouseMove event (otherwise major delay)
        Me.Text = "Drag on this form to draw on the receiving form"
      End Sub
    End Class

  12. #12

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    Hi passel,

    Well it is exactly what I'm looking for: A receiver.exe that is started from the x64 sender as soon as it needs it and not displaying an open form.

    Even if it's minimized it should be ok !

    I'm going to test the ReceiverProcess part you wrote in an adapted code, i.e. x64 sender has the wanted x64 DLL declared and the x32 receiver has the motion control DLL implemented.

    Cheers !

  13. #13

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    Hi Passel,

    I transformed the parts of this code successfully into what is my first step, i.e. "simple" communication between X64 and X86 app.

    Instead of the mouse coordinates I'm feeding the receiver with two integers taken from a pre process and display the values in txt boxes with invoke/delegates.

    I'm moving now to a more complex situation that needs some thinking:

    I'd like to do the same thing, BUT for double values.
    Lets call them X, Y and Z.

    X and Y range from -25 to +25 and must have 3 digits precisions : -20.001 / + 15.658 for example
    Z ranges from 0 to 150 and must have the same precision: 131.579 for example.

    It consists of positions in mm with a 1 micron precision.

    In your very useful example you put 2 integer values into one array of bytes. These integers had a 4 numbers length (CCD pixel position to be explicit).

    How could one proceed with 3 double values to be passed to the x86 app, knowing that their length will vary ? Separate them with a space or special char and then split ?

    Thanks again for your inputs !

  14. #14
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Working with an external 32bits dll in a x64 environment

    If you pass them as Doubles, then their length won't vary, they will always be 8 bytes.
    You would limit them to 3 digits of precision after the decimal point, if really necessary, when you use them.
    If you are taking string inputs with 3 digits after the decimal point, and setting a double to that, then the double will already be as close to the given number as it can be, and most likely will display as 3 digits of precision. You can always force the display to show the 3 digits when you convert to a string.

    The primary piece is converting the double to bytes, and back on the receiving side.
    The secondary piece, is how do you know when the data is valid, and if you want to have two or more messages, i.e. some with Integer Data, others with Float data, or just combine all the data into one message.
    I don't know your need, so I won't try to give an example of the "second" piece, but one way to resolve it would be to have some "header" information, perhaps as little as a single integer value that indicates a "Message ID", so you know what fields are in the message based on the ID, i.e. two 32-bit Integers, or three Doubles, and what the receiver is suppose to do with the data.

    For the primary piece, examples of storing and transmitting doubles should not be very different from the code that is transmitting Integers.
    Code:
          Dim b(3*8-1) As Byte  'three 8-byte (double) variables (X,Y and Z have to be declared as Doubles)
    
          Buffer.BlockCopy(BitConverter.GetBytes(X), 0, b, 0, 8) 'convert X to 8 byte array and copy to b(0..7)
          Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, b, 8, 8) 'convert Y to 8 byte array and copy to b(8..15)
          Buffer.BlockCopy(BitConverter.GetBytes(Z), 0, b, 16, 8) 'convert Y to 8 byte array and copy to b(16..23)
          udpSender.Send(b, b.Length, destIPnt)
    
    'And on the receiving side
        Dim X, Y, Z As Double
    
            X = BitConverter.ToDouble(b, 0)   'convert first 8 bytes (0..7) to double
            Y = BitConverter.ToDouble(b, 8)   'convert second 8 bytes (8..15) to double
            Z = BitConverter.ToDouble(b, 16)  'convert third 8 bytes (16..23) to double
    Hopefully, you can work out the details.
    Last edited by passel; Jun 10th, 2014 at 11:18 AM.

  15. #15

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    Hi, thx for the speed of the answer once again,

    I see where I was confusing things with doubles and bytes.

    I think I have to clarify though: the first part with the integers is not in the same app let's say. I don't have to choose between INT and DOUBLE, it's either one in the first part and the other in this case.
    But it does raise a question: if we want to transmit strings and doubles, how would you use the "header" info you were talking about ? If there is only strings and doubles, I can see the message being treated in the receiver part, but it's not clear to me how to implement it...

    Last thing, with the small range of values that I need (let's round it to -100.000 to 150.000), would it be better to use single instead of doubles and pass it on 4 bytes ? Is there really a gain ?

  16. #16
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Working with an external 32bits dll in a x64 environment

    Singles should work fine for numbers that don't exceed six significant digits, i.e. 150.312, is a small number in magnitude, but there are six digits of information there. Once you have seven digits, i.e. 1324.123, you can start to have rounding errors in the 7th digit. If you don't care, (for positional purposes it might not matter if the value is 1324.123 or 1324.122) and you're not doing a lot of calculations using singles (where rounding errors can accumulate quickly) then Singles should be fine. I only chose Doubles because you said you were dealing with Doubles in your post (#13).

    The header, in my mind, was something to use to identify the message, so you would know how to parse it.
    For instance, you mentioned earlier two example API function calls ("GroupMoveAbsolute and GroupMoveRelative functions").
    The parameters for those two functions have the exact same signature so if you were to use both, when you sent a message from the one computer to the other, how would the receiver know if it was to use the data from the message and call the GroupMoveAbsolute function, or call the GroupMoveRelative function.
    My point was if you are going to have different "formats" of the message to support different interfaces, you would normally have some "field" reserved, usually at the beginning of the message, that can uniquely identify what the message is so you know what data is in the message, and what to do with it, i.e. call the GroupMoveAbsolute, or GroupMoveRelative, or any number of other functions you will need to support.
    As an example, one interface I recently had to deal with used the first byte of the message as a message ID, so on the receiving side, I had processing that looked at that byte, and then took appropriate action based on the messaged ID.
    For instance, all of the messages were relatively short except for one, so all of them used a 2-byte value to indicated the size of the message, except the one used a 4-byte value to indicate the size of the message.
    So, my code read the first byte to get the message ID, and then if was the Big message, would read the size from the next 4 bytes, otherwise it read the size from the next 2 bytes.
    Then, once the size was known, the rest of the message could be read in, checksum calculated, and then based on the Message ID, a subroutine could be called to parse that particular message, knowing what values are expected to be in the message based on the established Interface Control Document.
    Code:
    Select Case msgID
      Case ThisMsg
        parseThisMsg(msgBuffer)
    
      Case ThatMsg
        parseThatMsg(msgBuffer)
    
      Case AnotherMsg
        parseAnotherMsg(msgBuffer)
    '....
    End Select
    I don't know what you need, but I would assume you must have more than one function you will be calling on the receiving side so may need to have some information from the transmitting side assuming the user interface is on the transmitting side of the code. I could be wrong.

    As for Strings, they are a little more intricate than the other fixed length types, so you have to deal with that. I'll show one way, which should work for your case since I'm assuming the strings you use will be based on ASCII (even though stored and processed as Unicode by VB.Net).
    Since the length can vary, you might want to have the string as the last thing transmitted in the message, but since we're packing and unpacking at the byte level, it really doesn't matter. If the string was not at the end of the message you would just want to use a variable to keep track of where you are inserting things in the message (i.e. all fields after the string will not be at a fixed offset within the message but will move up or down in address depending on the length of the string). Not a big deal, but if you only have one string to send, putting it at the end of the message will allow all the "fields" to start at a fixed offset.

    So, I'll just use snippets from my original drawing example to show adding a string to the message and decoding the string on the receiving side. You should be able to adapt those example lines to fit in however your message is going to be formatted.

    First on the transmitting side, we get the size of the string so we can size the buffer correctly. We also write that size to the buffer before we write the string so that the receiving side knows the length of the string it is receiving.
    Code:
    '
          Dim s As String = "ThisAtest"
          Dim sl As Integer = s.Length
          Dim b(11 + sl) As Byte '11 for X,Y and string size, + length of the string
          Buffer.BlockCopy(BitConverter.GetBytes(e.X), 0, b, 0, 4) 'convert X to 4 byte array and copy to b(0..3)
          Buffer.BlockCopy(BitConverter.GetBytes(e.Y), 0, b, 4, 4) 'convert Y to 4 byte array and copy to b(4..7)
          Buffer.BlockCopy(BitConverter.GetBytes(sl), 0, b, 8, 4) 'convert string length to 4 byte array and copy to b(8..11)
          Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(s), 0, b, 12, sl)  'convert string to array of ASCII bytes and copy to Buffer
          udpSender.Send(b, b.Length, destIPnt)  'send it out
    Now on the receiver, do the reverse to extract the data
    Code:
    'New variables added to support parsing string from the received data
        Dim s As String
        Dim sl As Integer
    
    '....
          b = socketIn.Receive(remoteIP)
          If b.Count > 12 Then 'expect 12 bytes plus string data
            Dim newPoint As Point
            newPoint.X = BitConverter.ToInt32(b, 0)  'convert first 4 bytes (0..3) to Int32 
            newPoint.Y = BitConverter.ToInt32(b, 4)  'convert second 4 bytes (4..7) to Int32
            sl = BitConverter.ToInt32(b, 8)  'get string length
            s = System.Text.ASCIIEncoding.ASCII.GetString(b, 12, sl) 'get ASCII bytes and convert to a string
            Me.Invoke(Sub() Me.Text = s)  'show the string received on the Form's title bar
    'etc....
    Last edited by passel; Jun 11th, 2014 at 06:31 PM.

  17. #17

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    Quote Originally Posted by passel View Post
    Singles should work fine for numbers that don't exceed six significant digits, i.e. 150.312, is a small number in magnitude, but there are six digits of information there. Once you have seven digits, i.e. 1324.123, you can start to have rounding errors in the 7th digit. If you don't care, (for positional purposes it might not matter if the value is 1324.123 or 1324.122) and you're not doing a lot of calculations using singles (where rounding errors can accumulate quickly) then Singles should be fine. I only chose Doubles because you said you were dealing with Doubles in your post (#13).
    I need a micrometric resolution on the translation stages so it would be better not to round/floor the values. Plus, I have some negative values for 2 stages as they go from -25 to +25mm so it may add a tricky aspect ?


    Quote Originally Posted by passel View Post
    The header, in my mind, was something to use to identify the message, so you would know how to parse it.
    For instance, you mentioned earlier two example API function calls ("GroupMoveAbsolute and GroupMoveRelative functions").
    The parameters for those two functions have the exact same signature so if you were to use both, when you sent a message from the one computer to the other, how would the receiver know if it was to use the data from the message and call the GroupMoveAbsolute function, or call the GroupMoveRelative function.
    The functions are indeed in the receiver. I only pass the doubles/string from the sender. Receiving part has the DLL (x86 one) attached to it and the functions are declared. AS you know, every time I need new coordinates I have to send them with the X64 sender and process them with the receiver which in turn calls a function. You are right about the fact that I have several different functions to call and that these functions can use the same arguments.

    Quote Originally Posted by passel View Post
    My point was if you are going to have different "formats" of the message to support different interfaces, you would normally have some "field" reserved, usually at the beginning of the message, that can uniquely identify what the message is so you know what data is in the message, and what to do with it, i.e. call the GroupMoveAbsolute, or GroupMoveRelative, or any number of other functions you will need to support.
    As an example, one interface I recently had to deal with used the first byte of the message as a message ID, so on the receiving side, I had processing that looked at that byte, and then took appropriate action based on the messaged ID.
    For instance, all of the messages were relatively short except for one, so all of them used a 2-byte value to indicated the size of the message, except the one used a 4-byte value to indicate the size of the message.
    So, my code read the first byte to get the message ID, and then if was the Big message, would read the size from the next 4 bytes, otherwise it read the size from the next 2 bytes.
    Then, once the size was known, the rest of the message could be read in, checksum calculated, and then based on the Message ID, a subroutine could be called to parse that particular message, knowing what values are expected to be in the message based on the established Interface Control Document.
    Code:
    Select Case msgID
      Case ThisMsg
        parseThisMsg(msgBuffer)
    
      Case ThatMsg
        parseThatMsg(msgBuffer)
    
      Case AnotherMsg
        parseAnotherMsg(msgBuffer)
    '....
    End Select
    Indeed, I need just that, a sign at the start of the transmitted byte array to differenciate the targeted functions. I have to count how many functions I really need to separate, but I guess it's less than 10, so a number from 1 to 9 could do the trick.


    Quote Originally Posted by passel View Post
    I don't know what you need, but I would assume you must have more than one function you will be calling on the receiving side so may need to have some information from the transmitting side assuming the user interface is on the transmitting side of the code. I could be wrong.
    It's exactly set up like that yes.

    Quote Originally Posted by passel View Post
    As for Strings, they are a little more intricate than the other fixed length types, so you have to deal with that. I'll show one way, which should work for your case since I'm assuming the strings you use will be based on ASCII (even though stored and processed as Unicode by VB.Net).
    Since the length can vary, you might want to have the string as the last thing transmitted in the message, but since we're packing and unpacking at the byte level, it really doesn't matter. If the string was not at the end of the message you would just want to use a variable to keep track of where you are inserting things in the message (i.e. all fields after the string will not be at a fixed offset within the message but will move up or down in address depending on the length of the string). Not a big deal, but if you only have one string to send, putting it at the end of the message will allow all the "fields" to start at a fixed offset.

    So, I'll just use snippets from my original drawing example to show adding a string to the message and decoding the string on the receiving side. You should be able to adapt those example lines to fit in however your message is going to be formatted.

    First on the transmitting side, we get the size of the string so we can size the buffer correctly. We also write that size to the buffer before we write the string so that the receiving side knows the length of the string it is receiving.
    Code:
    '
          Dim s As String = "ThisAtest"
          Dim sl As Integer = s.Length
          Dim b(11 + sl) As Byte '11 for X,Y and string size, + length of the string
          Buffer.BlockCopy(BitConverter.GetBytes(e.X), 0, b, 0, 4) 'convert X to 4 byte array and copy to b(0..3)
          Buffer.BlockCopy(BitConverter.GetBytes(e.Y), 0, b, 4, 4) 'convert Y to 4 byte array and copy to b(4..7)
          Buffer.BlockCopy(BitConverter.GetBytes(sl), 0, b, 8, 4) 'convert string length to 4 byte array and copy to b(8..11)
          Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(s), 0, b, 12, sl)  'convert string to array of ASCII bytes and copy to Buffer
          udpSender.Send(b, b.Length, destIPnt)  'send it out
    Now on the receiver, do the reverse to extract the data
    Code:
    'New variables added to support parsing string from the received data
        Dim s As String
        Dim sl As Integer
    
    '....
          b = socketIn.Receive(remoteIP)
          If b.Count > 12 Then 'expect 12 bytes plus string data
            Dim newPoint As Point
            newPoint.X = BitConverter.ToInt32(b, 0)  'convert first 4 bytes (0..3) to Int32 
            newPoint.Y = BitConverter.ToInt32(b, 4)  'convert second 4 bytes (4..7) to Int32
            sl = BitConverter.ToInt32(b, 8)  'get string length
            s = System.Text.ASCIIEncoding.ASCII.GetString(b, 12, sl) 'get ASCII bytes and convert to a string
            Me.Invoke(Sub() Me.Text = s)  'show the string received on the Form's title bar
    'etc....
    Do the string part has to be attached to the same array ? Only when the function needs both right ?

    For clarification:
    Some functions only need strings or stringbuilders as they need it in arguments and then return it with a new string in it:

    Code:
    Dim FirmwareVersion As New Stringbuilder(99)
    
    'and the function would later look sthg like:
    
    FirmwareversionGet(SocketID, FirmwareVersion)
    And some functions (like the move commands) need both string and doubles (string for the axis name to be moved and the targeted coordinates):

    Code:
    'declared like this:
    Declare Function GroupMoveAbsolute Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex as Integer, ByVal GroupName As String, ByVal NbElements As Integer, ByVal TargetPosition() As Double) As Integer
    
    'whereas the control of the status of the axis would be declared like:
    <DllImport("XPS_Q8_drivers.dll" ,CharSet:=CharSet.Ansi)> Private Shared Function GroupStatusStringGet(ByVal SocketIndex As Integer, ByVal GroupStatusCode As integer, ByVal GroupStatusString As StringBuilder) As Integer
    
    End Function

    Thanks again for your ideas and code snippets Passel !

    Cheers,

  18. #18

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    Hi ,

    I'm going to paste some of my code (functional as far as I know) as it should clear some areas.

    So here it is:

    1. The X64 Sender looks like this atmo

    vbnet Code:
    1. '
    2.  
    3. Public Class SendDoubles
    4.  
    5.     Dim udpSender As New UdpClient
    6.     Dim destIPnt As New IPEndPoint(IPAddress.Parse("127.0.0.1"), 12321)
    7.     Dim ReceiverProcess As Process
    8.  
    9.     Dim b() As Byte = {0}
    10.     Dim sl As Integer
    11.     Dim s As String
    12.  
    13.     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    14.  
    15.         ReceiverProcess = Process.Start(IO.Path.Combine(Application.StartupPath, "DoubleReceiveX86.exe"))
    16.  
    17.         'To ensure socket is open before the first use (otherwise major delay):
    18.         ' udpSender.Send(b, b.Length, destIPnt)
    19.         Me.Text = "Pass Doubles from X64 app ..."
    20.  
    21.     End Sub
    22.  
    23.  
    24.     Private Sub btnSEND_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSEND.Click
    25.  
    26.         Dim X, Y, Z, SRot, Theta, Omega, ZM As Double
    27.  
    28.        
    29.         X = CDbl(txtX.Text)
    30.         Y = CDbl(txtY.Text)
    31.         Z = CDbl(txtZ.Text)
    32.         SRot = CDbl(txtSRot.Text)
    33.         Theta = CDbl(txtTheta.Text)
    34.         Omega = CDbl(txtOmega.Text)
    35.         ZM = CDbl(txtZM.Text)
    36.  
    37.         s = txtINSTRING.Text
    38.         sl = s.Length
    39.  
    40.         ReDim Preserve b(61 + sl)
    41.  
    42.         If txtID.Text <> "" Then
    43.  
    44.             Dim numID As Byte
    45.             Dim SStr As String
    46.  
    47.             SStr = txtID.Text
    48.  
    49.             '  TRUE/FALSE on result
    50.             Dim resultat As Boolean = Byte.TryParse(SStr, numID)
    51.  
    52.             TextBox1.Text = resultat.ToString
    53.  
    54.             ' if true:
    55.             If resultat Then
    56.  
    57.                 Buffer.BlockCopy(BitConverter.GetBytes(numID), 0, b, 0, 1) 'convert ID status to 1 byte and copy to b(0)
    58.  
    59.                 Buffer.BlockCopy(BitConverter.GetBytes(X), 0, b, 1, 8)                  'convert X to 8 byte array and copy to b(1..8)
    60.                 Buffer.BlockCopy(BitConverter.GetBytes(Y), 0, b, 9, 8)                  'convert Y to 8 byte array and copy to b(9..16)
    61.                 Buffer.BlockCopy(BitConverter.GetBytes(Z), 0, b, 17, 8)                 'convert Z to 8 byte array and copy to b(17..24)
    62.                 Buffer.BlockCopy(BitConverter.GetBytes(SRot), 0, b, 25, 8)              'convert SRot to 8 byte array and copy to b(25..32)
    63.                 Buffer.BlockCopy(BitConverter.GetBytes(Theta), 0, b, 33, 8)             'convert Theta to 8 byte array and copy to b(33..40)
    64.                 Buffer.BlockCopy(BitConverter.GetBytes(Omega), 0, b, 41, 8)             'convert Omega to 8 byte array and copy to b(41..48)
    65.                 Buffer.BlockCopy(BitConverter.GetBytes(ZM), 0, b, 49, 8)                'convert ZM to 8 byte array and copy to b(49..56)
    66.                 Buffer.BlockCopy(BitConverter.GetBytes(sl), 0, b, 57, 4)                'convert sl to 4 byte array and copy to b(57..60)
    67.                 Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(s), 0, b, 61, sl)  'convert string s to array of ASCII Bytes and copy to b(61..sl)
    68.                 'Buffer.BlockCopy(BitConverter.GetBytes(s), 0, b, 61, sl)  'convert string s to array of ASCII Bytes and copy to b(61..sl)
    69.  
    70.                 'CALL FUNCTION SEND
    71.                 ' i.e. 64-bits -> 32bits:
    72.  
    73.                 udpSender.Send(b, b.Length, destIPnt)
    74.  
    75.             End If
    76.  
    77.         End If
    78.  
    79.     End Sub
    80.  
    81.  
    82.     Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
    83.  
    84.         ReceiverProcess.Kill()    'kill the application associated with ReceiverProcess object
    85.         ReceiverProcess.Close()   'free resources associated with ReceiverProcess object
    86.  
    87.     End Sub
    88.  
    89.  
    90. End Class
    Last edited by LiamC; Jun 24th, 2014 at 03:12 AM.

  19. #19

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    2. The X86 Receiver looks like this:

    vbnet Code:
    1. '
    2.  
    3. Imports System.Threading ' needed for separate threads
    4. Imports System.Net
    5. Imports System.Net.Sockets 'needed for UDP transfer
    6.  
    7. Imports Microsoft.VisualBasic
    8. Imports System.Runtime.InteropServices 'needed for <DLLIMport>
    9. Imports System.Text 'needed for Stringbuilder
    10.  
    11. Imports System.Globalization 'needed for cultureinfo
    12. Imports System.Math                         'needed for trigo + math operators
    13.  
    14.  
    15. Public Class DbleReceiver32bits
    16.  
    17.     ' /////////////////////////////////////////////////   DECLARATIONS XPS FUNCTIONS   ////////////////////////////////////////////////////////////
    18.  
    19.     Declare Function GetLibraryVersion Lib "XPS_Q8_drivers.dll" () As String 'dll version
    20.     Declare Function TCP_ConnectToServer Lib "XPS_Q8_drivers.dll" (ByVal Ip_Address As String, ByVal Ip_Port As Integer, ByVal TimeOut As Double) As Integer ' Initialize TCP Connection
    21.     Declare Function CloseAllOtherSockets Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex As Integer) As Integer '  Close all socket beside the one used to send this command
    22.     Declare Function KillAll Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex As Integer) As Integer '  Put all groups in 'Not initialized' state
    23.  
    24.  
    25.     Declare Function ControllerStatusGet Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex As Integer, ByVal ControllerStatus() As Integer) As Integer '  Get controller current status and reset the status
    26.     Declare Function GroupStatusGet Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex As Integer, ByVal GroupName As String, ByVal GroupStatus() As Integer) As Integer '  Return group status
    27.  
    28.     Declare Function GroupInitialize Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex As Integer, ByVal GroupName As String) As Integer '  Start the initialization
    29.     Declare Function GroupHomeSearch Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex As Integer, ByVal GroupName As String) As Integer '  Start home search sequence
    30.  
    31.     Declare Function GroupMoveAbsolute Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex As Integer, ByVal GroupName As String, ByVal NbElements As Integer, ByVal TargetPosition() As Double) As Integer '  Do an absolute move
    32.     Declare Function GroupMoveRelative Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex As Integer, ByVal GroupName As String, ByVal NbElements As Integer, ByVal TargetDisplacement() As Double) As Integer '  Do a relative move
    33.     Declare Function GroupPositionCurrentGet Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex As Integer, ByVal GroupName As String, ByVal NbElements As Integer, ByVal CurrentEncoderPosition() As Double) As Integer '  Return current positions
    34.     Declare Function GroupMoveAbort Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex As Integer, ByVal GroupName As String) As Integer '  Abort a move
    35.  
    36.  
    37.     Declare Function XYLineArcExecution Lib "XPS_Q8_drivers.dll" (ByVal SocketIndex As Integer, ByVal GroupName As String, ByVal TRJFileName As String, ByVal Velocity As Double, ByVal Acceleration As Double, ByVal NumberOfTimes As Integer) As Integer '  Return current positions
    38.  
    39.  
    40.     '-----------------------------------------------------
    41.     'If the function needs a stringbuilder, syntax changes to:
    42.  
    43.     ' FirmwareVersionGet:
    44.     <DllImport("XPS_Q8_drivers.dll", CharSet:=CharSet.Ansi)> Private Shared Function FirmwareVersionGet(ByVal SocketIndex As Integer, ByVal Version As StringBuilder) As Integer
    45.  
    46.     End Function
    47.  
    48.     ' ControllerStatusStringGet:
    49.     <DllImport("XPS_Q8_drivers.dll", CharSet:=CharSet.Ansi)> Private Shared Function ControllerStatusStringGet(ByVal SocketIndex As Integer, ByVal ControllerStatusCode As Integer, ByVal ControllerStatusString As StringBuilder) As Integer '  Return the controller status string
    50.  
    51.     End Function
    52.  
    53.     ' GroupStatusStringGet:
    54.     <DllImport("XPS_Q8_drivers.dll", CharSet:=CharSet.Ansi)> Private Shared Function GroupStatusStringGet(ByVal SocketIndex As Integer, ByVal GroupStatusCode As Integer, ByVal GroupStatusString As StringBuilder) As Integer '  Return the group status string corresponding to the group status code
    55.  
    56.     End Function
    57.  
    58. ' ////////////////// DECLARATIONS
    59.     Public IPAddress1 As String
    60.  
    61.     Public IPPort As Integer
    62.     Public SocketID As Integer
    63.  
    64.     Public errors(5) As Integer
    65.  
    66.     Public XYStatus() As Integer
    67.     Public ZStatus() As Integer
    68.     Public SROTStatus() As Integer
    69.     Public THETAStatus() As Integer
    70.     Public OMEGAStatus() As Integer
    71.     Public ZMStatus() As Integer
    72.  
    73.  
    74.     '' GROUP/CONTROLLER STATUS LIST:
    75.     Public Const MovingCode = 44
    76.     Public Const JoggingCode = 47
    77.     Public Const ReadyStateAfterAbort = 10
    78.     Public Const ReadyStateFromMotion = 12
    79.  
    80.     Dim AlreadyINITCodes() As Integer
    81.     Public ListINITCodes As List(Of Integer)
    82.  
    83.     Public INITSTATE As Boolean
    84.  
    85.     ' First small Mirror:
    86.     Public BasculMirrorState As Boolean = False
    87.  
    88.     Public PosiMirrorON() As Double
    89.     Public PosiMirrorOFF() As Double
    90.  
    91.     Public XYStatusString As New StringBuilder(99)
    92.     Public ZStatusString As New StringBuilder(99)
    93.     Public SROTStatusString As New StringBuilder(99)
    94.  
    95.     Public THETAStatusString As New StringBuilder(99)
    96.     Public OMEGAStatusString As New StringBuilder(99)
    97.     Public ZMStatusString As New StringBuilder(99)
    98.  
    99.  
    100.     Dim receiveThread As New Thread(AddressOf Receiver) ' other thread points to Address
    101.  
    102.  
    103.     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    104.  
    105.         Me.Text = "...to this X86 form for processing"
    106.  
    107.         ' INIT XPS HERE:
    108.         IPAddress1 = "192.168.0.254"
    109.         IPPort = 5001
    110.         'Buffer1 = ""
    111.  
    112.         ' XPS DLL functions
    113.         SocketID = TCP_ConnectToServer(IPAddress1, IPPort, 20)
    114.  
    115.         'RAZ:
    116.         errors(0) = KillAll(SocketID)
    117.  
    118.         'tri-axis / XY:
    119.         errors(0) = GroupInitialize(SocketID, "XY")
    120.         errors(0) = GroupHomeSearch(SocketID, "XY")
    121.  
    122.         'tri-axis /Z:
    123.         errors(1) = GroupInitialize(SocketID, "Z")
    124.         errors(1) = GroupHomeSearch(SocketID, "Z")
    125.  
    126.         ' flipping mirror:
    127.         errors(2) = GroupInitialize(SocketID, "SROT")
    128.         errors(2) = GroupHomeSearch(SocketID, "SROT")
    129.  
    130.  
    131.         ' Starts the second thread:
    132.         receiveThread.IsBackground = True
    133.  
    134.         receiveThread.Start()
    135.  
    136.     End Sub
    137.  
    138.  
    139.     Private Sub Receiver()
    140.  
    141.         ' Runs on another thread:
    142.         Dim socketIn As New UdpClient(12321)  'Listen on port UDP port 12321
    143.         Dim remoteIP As New IPEndPoint(IPAddress.Any, 0)  'will hold who sent the data
    144.         Dim b() As Byte 'buffer to receive data
    145.  
    146.         Dim X1, Y1, Z1, SRot1, Theta1, Omega1, ZM1 As Double
    147.         Dim ID, sl As Integer
    148.         Dim s As String
    149.  
    150.         Do
    151.             b = socketIn.Receive(remoteIP)
    152.  
    153.             If b.Count > 60 Then
    154.  
    155.                 ' mesage decoding:
    156.                 ID = b(0)    'read first 1 byte b(0) (Integer)
    157.                 X1 = BitConverter.ToDouble(b, 1)   'convert first 8 bytes (1..8) to double
    158.                 Y1 = BitConverter.ToDouble(b, 9)   'convert second 8 bytes (9..16) to double
    159.                 Z1 = BitConverter.ToDouble(b, 17)  'convert third 8 bytes (17..24) to double
    160.                 SRot1 = BitConverter.ToDouble(b, 25)  'convert fourth 8 bytes (25..32) to double
    161.                 Theta1 = BitConverter.ToDouble(b, 33)  'convert fifth 8 bytes (33..40) to double
    162.                 Omega1 = BitConverter.ToDouble(b, 41)  'convert sixth 8 bytes (41..48) to double
    163.                 ZM1 = BitConverter.ToDouble(b, 49)  'convert seventh 8 bytes (49..56) to double
    164.                 sl = BitConverter.ToInt32(b, 57)     'get string length
    165.                 s = ASCIIEncoding.ASCII.GetString(b, 61, sl) 'get ASCII bytes and convert to a string
    166.  
    167.                 ' depending on ID , different answers:
    168.  
    169.                 ChangeTextOf(TextBox1, X1.ToString("00.000", CultureInfo.InvariantCulture))
    170.                 ChangeTextOf(TextBox2, Y1.ToString("00.000", CultureInfo.InvariantCulture))
    171.                 ChangeTextOf(TextBox3, Z1.ToString("000.000", CultureInfo.InvariantCulture))
    172.                 ChangeTextOf(TextBox4, SRot1.ToString("000.000", CultureInfo.InvariantCulture))
    173.                 ChangeTextOf(TextBox5, Theta1.ToString("000.000", CultureInfo.InvariantCulture))
    174.                 ChangeTextOf(TextBox6, Omega1.ToString("000.000", CultureInfo.InvariantCulture))
    175.                 ChangeTextOf(TextBox7, ZM1.ToString("000.000", CultureInfo.InvariantCulture))
    176.                 ChangeTextOf(txtLENGTH, sl.ToString)
    177.                 ChangeTextOf(txtSTRING, s)
    178.  
    179.                 Dim PosXY(1), PosZ(0) As Double
    180.                 Dim CurrentPos(1) As Double
    181.  
    182.                 GroupPositionCurrentGet(SocketID, "XY", 2, CurrentPos)
    183.  
    184.                 Select Case ID
    185.  
    186.                     Case 1
    187.                         ' X translation stage will be moved
    188.                         ChangeTextOf(txtID, ID.ToString("0", CultureInfo.InvariantCulture))
    189.                         MsgBox("vous avez demandé à bouger la platine X", MsgBoxStyle.Information, "Code ID=1")
    190.  
    191.                         PosXY(0) = X1
    192.                         PosXY(1) = CurrentPos(1)
    193.                         GroupMoveAbsolute(SocketID, "XY", 2, PosXY)
    194.  
    195.                     Case 2
    196.                         '  Y
    197.                         ChangeTextOf(txtID, ID.ToString("0", CultureInfo.InvariantCulture))
    198.                         MsgBox("vous avez demandé à bouger la platine Y", MsgBoxStyle.Information, "Code ID=2")
    199.  
    200.                         PosXY(0) = CurrentPos(0)
    201.                         PosXY(1) = Y1
    202.                         GroupMoveAbsolute(SocketID, "XY", 2, PosXY)
    203.  
    204.                     Case 3
    205.                         '  Z :
    206.                         ChangeTextOf(txtID, ID.ToString("0", CultureInfo.InvariantCulture))
    207.                         MsgBox("vous avez demandé à bouger la platine Z", MsgBoxStyle.Information, "Code ID=3")
    208.  
    209.                         PosZ(0) = Z1
    210.                         GroupMoveAbsolute(SocketID, "Z", 1, PosZ)
    211.  
    212.                     Case 4
    213.                         ' Mirror will me moved
    214.                         ChangeTextOf(txtID, ID.ToString("0", CultureInfo.InvariantCulture))
    215.                         MsgBox("vous avez demandé à bouger la platine Miroir", MsgBoxStyle.Information, "Code ID=4")
    216.  
    217.  
    218.                     Case 5
    219.                         ' THETA stage:
    220.                         ChangeTextOf(txtID, ID.ToString("0", CultureInfo.InvariantCulture))
    221.                         MsgBox("vous avez demandé à bouger la platine THETA", MsgBoxStyle.Information, "Code ID=5")
    222.  
    223.  
    224.                     Case 6
    225.                         ' OMEGA stage:
    226.                         ChangeTextOf(txtID, ID.ToString("0", CultureInfo.InvariantCulture))
    227.                         MsgBox("vous avez demandé à bouger la platine OMEGA", MsgBoxStyle.Information, "Code ID=6")
    228.  
    229.  
    230.                     Case 7
    231.                         ' ZM translation stage:
    232.                         ChangeTextOf(txtID, ID.ToString("0", CultureInfo.InvariantCulture))
    233.                         MsgBox("vous avez demandé à bouger la platine ZM", MsgBoxStyle.Information, "Code ID=7")
    234.  
    235.  
    236.                 End Select
    237.  
    238.                 ' 2. refresh form
    239.                 Me.BeginInvoke(Sub() Me.Invalidate())
    240.  
    241.             End If
    242.  
    243.         Loop
    244.  
    245.     End Sub
    246.  
    247.  
    248.     ' ///////
    249.     ' DELEGATES & INVOKES
    250.  
    251. ' Act on an UI object from another thread:
    252.  
    253.     Delegate Sub ChangeTextOfZoneDelegate(ByVal Ctrl As Control, ByVal Text3 As String)
    254.  
    255.     Public Sub ChangeTextOf(ByVal ctrl As Control, ByVal Text3 As String)
    256.  
    257.         If ctrl.InvokeRequired Then
    258.  
    259.             Dim CTRLDel As New ChangeTextOfZoneDelegate(AddressOf ChangeTextOf)
    260.  
    261.             Me.Invoke(CTRLDel, New Object() {ctrl, [Text3]})
    262.  
    263.         Else
    264.  
    265.             ctrl.Text = Text3
    266.  
    267.         End If
    268.  
    269.     End Sub
    270.  
    271.  
    272.  
    273. End Class


    To sum-up:

    - It sends DOUBLE and STRING through UDP with the X64 sender (directly inspired from your snippets obviously)
    - It reads the incoming bytes in the X86 app and selects actions depending on the ID integer at the start of the bytes array (Case Select as you mentionned)
    - The external DLL functions are implemented and will move some micrometric stages depending on the selected actions (TCP_Connect,GroupInit, GroupHome, GroupMoveAbsolute, etc.)
    - It will update text zones and button from another thread if needed (delegate/invoke)

    Again, thanks a lot Passel, the first big part is completed (have to implement the X64 code in a bigger code containing image processing functions now) and that has a lot of things to do with your help !


    Now, I have to move on to the directly related subject of:

    - Sometimes I need to ask the stages "What is your current position on Axis number N (1 to 7) ?" and get the answer to move accordingly to this current position (often a relative movement: +/- xx µm)

    So this triggers the big question:

    HOW does the X86 receiver responds in return to a request by being the sender this time ? (as the X64 app will definitely need some info from the micrometric stages along the way...)

    Thx a million !
    Last edited by LiamC; Jun 24th, 2014 at 03:12 AM.

  20. #20
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Working with an external 32bits dll in a x64 environment

    I started responding to this yesterday but came down with a 24-hour bug which pretty much ending working on anything the rest of the day.
    I'm pretty much back to normal now.
    A couple of minor comments on the X86 sender.
    Is there a reason you made the byte array, b, a class level scope?
    And there is no reason to do a Preserve when redimensioning it since you are going to be clobbering all the values anyway.
    Also, ReDim Preserve b(61 + sl), means you are transmitting one extra unused byte in your packet.
    The first character of the string would go in index 61, so if the string was only 1 ASCII character, then the last index would be 61. Since you're adding the string length, i.e. 1, to the 61, you actually transmit 62 bytes. It should be (60 + sl), the 60 meaning how many spots in the array are used up to this point, not including the string being added.

    Personally, I generally just use a local method scope array, so would be Dim b(60 + sl) in the local sub, rather than ReDim. ReDimming a class level array continually is going to create more memory thrashing that has to be managed, than just using a local array in a method.
    But if you want a class level array for some reason, I would remove the Preserve option, as that just adds unneeded overhead.

    Also, since numID is already a byte, you don't have to go through all that BlockCopy..GetBytes overhead.
    I would change the following line of code:
    Buffer.BlockCopy(BitConverter.GetBytes(numID), 0, b, 0, 1) 'convert ID status to 1 byte and copy to b(0)
    to this:
    b(0) = numID

    As for transmitting data the other direction, you would do the same as you have in the one direction.
    You can reuse the sockets you already have. Since both programs are running on the same machine, I would use a different port number for the X64 receiver.

    So, on the X64 side, you can change your socket initialization to indicate what you want to bind the socket to a port to listen to.
    Change "Dim udpSender As New UdpClient" to "Dim udpSender as New UdpClient(12320)"

    You can copy some of the code from the x86 receiver to set up a background thread that will read from the udpSender object.
    Then on the X86 side, when you call the API to get the values, pack them in a byte array and send them out reusing the UdpClient object you already have.
    In order to use the UdpClient you already have in the X86 Receiver, you will need to move the declaration out of the method scope, "Sub Receiver", and put it at class scope, so move the declaration:
    Dim socketIn As New UdpClient(12321) 'Listen on port UDP port 12321
    up to where your other class scope declarations, e.g. Dim receiveThread, are located.

    So, for example, on the x86 side I would prepare the udpClient like this:
    Code:
    Dim socketIn As New UdpClient(12321)                                'Listen on UDP port 12321
    Dim destIPnt As New IPEndPoint(IPAddress.Parse("127.0.0.1"), 12320) 'Send to port 12320
    and on the x64 side, this:
    Code:
      Dim udpSender As New UdpClient(12320)                               'Listen on UDP port 12320
      Dim destIPnt As New IPEndPoint(IPAddress.Parse("127.0.0.1"), 12321) 'Send to UDP port 12321
    If you copy the Sub Receiver code from the x86 project, all you should need to change to have it receive data is the UdpClient name.
    Change "b = socketIn.Receive(remoteIP)" to "b = udpSender.Receive(remoteIP)".

    Now that it can receive data, you will just need to decide how you want to layout the data to be sent, and add code on the sending and receiving sides to process it.

  21. #21

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    Hi Thanks for your answer,

    I've read through it only once and quite rapidly but as I've understood:

    1. The fact the receiver already has a thread waiting for some input data doesn't prevent it from sending output (formatted) data through another port with ANOTHER thread. Correct ?

    2. I think there should be some clarification about the way things should be done as I've not been exhaustive:

    When the receiver (X86) has to send data back (typically, it answers the question "what is your position on this precise axis ?" so we can have relative moves from this position, or decide that is not the right position and move again, etc.), the X64 would have to wait for the answer before going further in the code.

    Maybe somekind of synchronous way could be better ?

    I.e. : As long as SenderX64 hasn't receive anything in return from X86 sender, the code doesn't continue. Would it be achievable with the threading tools (thread1 stops as long ass thread2 hasn't flagged) ?

    Cheers,

  22. #22
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Working with an external 32bits dll in a x64 environment

    1. A socket is set up for two way communication and the sending and receiving data through a socket are independent, so yes one thread can be sitting on the read waiting for anything to come in, while data can be sent out through the same socket from another section of code, in my case, that is usually the User Interface. So various messages may be sent out the socket in responses to user input on buttons, and what not, while the background task receives and processes any messages coming in.

    If there is a Request<->Response situation, I usually would handle that in a more modular way.
    Rather than have a linear block of code that is a series of request, wait, process, request, wait, process...
    the logic would be divided into steps, with a step usually corresponding to a sub or function.
    So, some code, perhaps in a button, could start the process by initiating a request and that is all.
    When the request has been processed by the remote machine and the response returned, the receiver could then forward that response to some method to process the response. That code could determine if another command should be sent, and/or another request.
    Again, if the request goes out, then the method will be called again when the response comes back, so can process another feedback.

    This type of processing is essentially a state machine. Each time a response is received, the method associated with processing that response is called, and depending on the state of the operation at that point in time, the code acts on the information, and sets up for the next state, and exits.

    Now, perhaps you already have a large amount of code that is based on the fact that the API calls you do to the driver are by design synchronous. If you query the API about position, the code is hung until the call returns with the information.
    If you want that to be the case, then you can do that.
    Rather than have a background thread that reads from the socket, processing everything received in one place, you could do away with that, and just create a method that you would call to do the read when needed.
    That way, you could send the request, call the read method, and your code would be hung until the response was received (or the read timedout, if you specified a timeout).

  23. #23

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    The driver functions are indeed synchronous, or what I call synchronous so I'll define it briefly:

    If I use GroupMoveAbsolute to move an axis, let's say X, the controller will proceed with a command to the translation stage. The stage will then move to the desired position. The controller will check the position and return an error status (0= no error, ...). ONLY THEN, will it proceed the next command, let's say another GroupMoveAbsolute on the Z axis.

    To Sum up:

    Code:
    GroupMoveAbsolute (socketID,"X",1,destination1)
    GroupMoveAbsolute (socketID,"Z",1,destination2)
    Will have axis X go to destination1 (can take 2-3 seconds if it's at the other end of the translation stage, position checked by controller, ONLY THEN, axis Z go to destination2.

    As for the second question, I have 6 functions in the RECEIVER part that will return data to the SENDER:

    1. GroupPositionCurrentGet => returns a Double Array (=position() as double). As there can be more than one axis in a Group, sometimes has two dimensions.
    Example: "XY" as a Group, returns position(1) : posX for the 0 index and posY for index 1)
    2. GroupStatusGet => returns an Integer Array, always size : CodeStatus(0)
    3. TriAxeStatus returns an Integer Array, always the same size: Status(2)
    4. MirrorStatus returns an Integer Array, always the same size: MStatus(2)
    5. GroupStatusStringGet: returns a String and needs a stringbuilder in argument
    6.FirmwareVersionGet: returns a String and needs a stringbuilder in argument


    Thanks !
    Last edited by LiamC; Jun 26th, 2014 at 09:44 AM.

  24. #24

    Thread Starter
    Addicted Member
    Join Date
    Sep 2013
    Posts
    144

    Re: Working with an external 32bits dll in a x64 environment

    Hi Passel,

    I will paste some of my modified code as I've managed to get the example working both ways: x64->x86 and back x86->x64.

    For the moment, as I think you mentionned, the start of the example program is a send button using the udp connection initialized in the load of the x64 app.

    When I change some values (either destination as Double or ID as byte) all goes well, i.e. the targeted stages do move accordingly to these changes values.
    When I click again on the SEND button again, all goes well.

    I tried to send back two quick commands (the function is called Back2UDP to send from x86 back to x64).
    I got the two string from the two commands, so everything is good too.

    Of course the main (future) app will have an equivalent to the SEND button to start.

    Correct me if i'm wrong, but if I want to implement it without clicking on a SEND button again, all I have to do is call the Send2UDP function again (x64->x86) where I want it ?
    It worked with the Back2UDP function with 2 successive calls in a row, but this function didn't have to move axis and wait for them to finish their motion...


    Thanks !

  25. #25
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Working with an external 32bits dll in a x64 environment

    Quote Originally Posted by LiamC View Post
    ...1
    I tried to send back two quick commands (the function is called Back2UDP to send from x86 back to x64).
    I got the two string from the two commands, so everything is good too.
    ...2
    Correct me if i'm wrong, but if I want to implement it without clicking on a SEND button again, all I have to do is call the Send2UDP function again (x64->x86) where I want it ?
    ...3
    It worked with the Back2UDP function with 2 successive calls in a row, but this function didn't have to move axis and wait for them to finish their motion...
    1. I assume that means you went with a background thread on the x64 side that is receiving the data, rather than a synchronous read that you call on the x64 side when you expect data.

    2. That is correct (assuming the code is correct). Your code should be able to change values and send the messages as needed as fast as the interface supports and the receiver can process. Easily thousands of times per second if necessary.

    3. That kind of ties in to #1.
    If the x64 side knows that the x86 side is not going to generate unsolicited messages, i.e. the only time the x68 side would transmit data to the x64 side is when the x64 sent a message to the x86 side, initiating the request, then you could use a purely synchronous process where you only do a read when you expect a response:
    x64 -> Send Command (no response expected)
    x86 -> Receives and process command, does not send a response
    x64 -> Send Command (no response expected)
    x86 -> Receives and processes command, does not send a response
    x64 -> Send Command (expect a response)
    x64 -> call sub to do a udpClient.Receive (x64 thread now hangs until data received, or timeout if defined)
    x86 -> Receives command, processes command, gets response from API, and sends response to x64
    x64 -> Receives the response, updates variables and returns from sub.
    x64 -> Processes the expected response
    x64 -> ....continues

    Now, you could tightly couple the two by deciding that every command send to the x86 will get a response, rather than having to distinguish between commands with responses and commands without.
    A large number of those responses may just be simple acknowledgments to indicate the command was receive, the API call done, and returned. You send routine on the x64 side could be written as a two step method:
    X64-> Call the SendUDP method
    X64.SendUDP -> Send the message
    X64.SendUDP-> Call UDPReceive method
    X64.UDPReceive-> Receive message from X86, store returned value and return.
    X64.SendUDP-> Return
    X64-> If no data expected (only ack), then continue else if data expected, process data and continue

    Of course there are quite a few options on how to do this.
    The main issue I have with the above is the hanging of the thread waiting for responses. I would not want to hang the GUI, as a rule, so I would use a background task for all this processing and synchronized waiting.
    There are a number of ways background tasking can be accomplished, using background worker objects for instance, and even newer methods have been added with more recent releases of the .Net framework which I haven't even begun to explore.
    I have to admit that I still haven't used a background worker yet. The type of work I do doesn't involve a process where you have a defined tasks that you want to kick off, have it run to some conclusion and finish.
    I usually have to write code that interacts with the user (a GUI), and communicates with one or more computers running various software simulations or control systems.
    So, the design usually comes down to the GUI thread interacting with the user, and one or more permanent background threads doing communication and interface processing. Some interfaces I've worked with are critical in the sense that if you didn't update the data being transmitted for a period of more than 40ms, error messages would be generated and flagged on instruments that would have to be acknowledged by the user, which was unacceptable. So you definitely couldn't be using a timer on the GUI thread, or be tied to the GUI thread in any manner because in Windows, simply clicking on the titlebar of the form can hang the GUI thread up to nearly 1/2 a second (400-500ms), where no user code is being process, and no timer events generated.
    A background thread the will continue to process regardless of user GUI interactions was critical in that situation.
    I currently have code that is a TCP server to one process on another computer, a TCP client to a process on yet another computer, and a UDP receiver from a process on a third computers, and UDP sender to a forth process (may or may not be on one of the other three computers), as well as the GUI interface for control and monitoring of the device being simulated.
    Using a few background threads to separate and manage these responsibilities is what makes the code easy to write and maintain and work reliably.

    Don't want to overwhelm you. In your case, it may not matter if the GUI is tied up for several seconds during some operations, but if the process may need to be interrupted, and has that capability through some secondary control input, then you may not want to stall your GUI during the operations.
    Probably a lot to think about. I'll stop at this point.

  26. #26
    New Member
    Join Date
    Nov 2017
    Posts
    1

    Re: Working with an external 32bits dll in a x64 environment

    Hi @LiamC and @passel. It's been a while since this thread has been visited, but I have the exact same trouble with the XPS Newport Stage and 32-bit drivers that I need to use with 64-bit code. Any update on what solution was reached here? To date, Newport still does not provide 64-bit drivers.

  27. #27
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,988

    Re: Working with an external 32bits dll in a x64 environment

    You may want to just start a new thread on this one. The key to the forum is to get the right set of eyes on the question, which means getting the most eyes on the question. While you are asking about something that is clearly related to this thread, the thread, as you noted, is fairly old. Many of the relevant parties are still pretty active, so they may see and respond, but I still think you'd be better off starting a new thread. You might even make a link to this thread, or not, since you also suggested that the thread didn't provide a solution for you.
    My usual boring signature: Nothing

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width