Jump to content

Rob Calhoun

Members
  • Posts

    33
  • Joined

  • Last visited

  • Days Won

    3

Posts posted by Rob Calhoun

  1. I am extremely disappointed with it. The suspend-when-called bug introduced in the initial LV 2009 release is still not fixed in SP1. This bug was noted on LavaG many months ago (see this post) and submitted to NI before the SP1 beta started. I reported it again during the SP1 beta. Months pass, the release is out, and suspend-when-called is still completely broken. You still cannot change control values and it still corrupts the control data when the VI is executed, which means that you cannot run a suspended VI more than once without corrupting your data. It's a ghastly bug.

    I am dumbfounded that a data loss issue like this was not considered a blocker for SP1's release.

    Here is an example of the suspend-when-called bug corrupting data.

    BadBufferReuse(192207).zip

  2. I am unsure how to set the Local IP there doesn't seem to be a way to do this, only a way to read it. This is going to be a problem...

    You generally do not need to set an IP address when you create a server, just a port. The address input is there only so that you can force a machine with multiple IP addresses to listen on only a specific IP. The default is "listen on all IP addresses".

    As others have said, there are good examples of how to use the TCP primitives. Basically you create a listener, wait on the listener for a connection, then service the connection. The one thing that is not very clear is that you do not need to create a listener more than once; one listener can handle an unlimited number of connected clients. Typically you pass a cluster with the TCP refnum and other data (e.g. "New Cnx") to a new re-entrant process that runs top-level so that the listener thread can go back to the wait-on-listener state.

    The code below is simpler than that; rather than spawn off a server process, it just dumps a datetime message to whomever connected on port 27000 (via e.g. "telnet localhost 27000" and then hangs up the TCP connection. With this type of design you can only have one client connected at a time. Once done with the current client, it will go back to waiting on listener. To exit the program cleanly we use the close TCP function to close the listener, causing Wait On Listener to error out.

    post-15310-125857461615_thumb.png

    -Rob

  3. I encountered this bizarre and performance-destroying behavior yesterday so I thought I should write up some notes on it.

    In our application we call various Microsoft components (Active Data Objects, WinHTTP, MSXML) to do tasks that aren't build-in to LabView (database connectivity, http and xpath queries, respectively.) We use the Automation (ActiveX) functions for this. When we expect to call these routines in parallel, their wrappers are set for re-entrant execution.

    I was running a process that does a lot of downloading via WinHTTP in one thread while doing a lot of opening and closing of databases in another. I was getting extremely poor performance. A little debugging isolated the problem to a block on the Automation Open function (on an ADO object!) while WinHTTP was doing a synchronous download. Adding more parallel downloads makes the problem worse; I have no trouble firing off 5 WinHTTP downloads in parallel, but doing so almost completely lobotomizes LabView. Click the run button with five WinHTTP clones chomping away at big files and even the simplest routines (see two-plus-two.png) get completely stuck.

    The solution is simple: enable asynchronous downloads. From the WinHttpRequest documentation.

    "The main advantage to using WinHTTP asynchronously in script is that the Send method returns immediately. The request is prepared and sent by a worker thread. This enables your application to do other things while it is waiting for the response. "

    We had set it to synchronous because a) it was the default and b) we were running it in a clone anyway and didn't really want the clone to do anything other than wait for the download to complete. What I did not realize is that when Microsoft says "[t]his enables your application to do other things while it is waiting for the response", they are not kidding! The WinHttpRequest.Send method apparently has deep ties into the depths of the Windows execution system and you REALLY cannot do other things while it is running.

    The strangest part of the whole business is that while you can set up an ActiveX event to catch the completion notice, if you don't mind you VI (as opposed to your entire application) to get blocked, the easy way to do a download is to fire off the request with WinHttpRequest.Send (async) and then wait (synchronously) for it to complete or timeout using WinHttpRequest.WaitForResponse. WinHttpRequest.WaitForResponse blocks your VI but it doesn't destroy your world.

    So in the end this had an easy solution. (And I was really sweating this last night!) This kind of issue, however, is why Jim and I would like to see a nice SSL-capable http client built in to LabView. (see Setting up SSL Web Services).

    Here is the synchronous (blocking) code. DON'T DO THIS!

    Here is the non-blocking) code. You can use an ActiveX event if you want to show a progress bar or something, but this will get the job done.

    -Rob Calhoun

    blocked!

    post-15310-124966470219_thumb.png

    blocking:

    post-15310-12496647159_thumb.png

    non-blocking:

    post-15310-124966472783_thumb.png

  4. I agree with the other posters on general good style for calling DLLs from LabView with non-simple types: allocate the memory in LabView, pass a pointer to the LV-allocated buffer to the DLL as an argument (usually along with a length) and allow the DLL to modify the buffer that was passed in. So a C function that converted a lower-case string to upper case might have prototype:

    int ConvertToUpperCase (char *string, int length)

    where the return value is used for error handling.

    Some DLLs return a pointer to data in the return value. It's not ideal but sometimes you have to work with what your are given. There is no way to access this directly using the Call Library Function, but there is a workaround that is described in this post

    http://forums.lavag.org/How-to-get-data-fr...aded#entry38166

    which links to this example

    http://zone.ni.com/devzone/cda/epd/p/id/3672

    The "secret trick" (thanks, Rolf!) that you do not need to write your own DLL to do this; the LabView dev env and runtime export a "MoveBlock" function that you can use to copy data from an area of memory that is private to the DLL to an area of memory allocated by LabView. (Since the DLL is loaded by LabView, it isn't an access violation to READ the data, but chaos will result if LabView tries to MODIFY the data.) You return a pointer from your function and then use MoveBlock to copy that into an area of LV-allocated memory, which LabView can then copy/move/delete as it wishes. In the function above, you'd have to call MoveBlock multiple times, once for each pointer dereference required. (Yuck!!!) As others have pointed out, plenty can go wrong if the internal data changes while you are doing this (for example, it is device driver). In the example below, I throw a mutex around the calls to prevent me from inadvertently calling the library with the same instance in a different VI.

    Something that isn't clear is what data type to return pointers. Lately I have been using the Call Library Function's "unsigned pointer-sized integer" to handle pointers and size_t integers. On my platform this always returns a UInt64. In fact the function I am calling works with size_t ints set to either "pointer sized" or Uint32s. This would not be the case if LabView were actually passing U32s or U64s at my request---any parameters following the first goof would be corrupted. LabView's documentation is pretty sparse on this, but my guess is that since wires size needs to be defined at compile time, the compiler always allocates 64 bits of storage for "pointer sized integers" on the diagram, but inside the call library function it passes either 32 or 64 bits as appropriate. I don't think this "pointer-sized integer" is foolproof because it requires LabView and the DLL to agree on what that is, but it seems safer than blindly assuming a U32.

    If you have source code for the DLL, you can wrap it with your own code and define fixed-size types.

    post-15310-1242314904.png?width=400

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.