Jump to content

Rob Calhoun

  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by Rob Calhoun

  1. The UAC in Vista and later is designed to prevent even administrator accounts from doing "dangerous" things without user confirmation. Running as Administrator alone is not sufficient to avoid the prompt as Microsoft has tried very hard to ensure that the prompts cannot be worked around. (This is so that virus authors can't go that route.) The "right" way to handle this is to ship a manifest that tells Windows that an application needs elevation. It will ask for it on launch. If there is no manifest, Windows tries to figure out which applications require elevation (for example, an installer named "setup.exe") and request elevation from the user on launch. It is very frequently wrong and this results in cascade of errors later on. I agree with you that disabling UAC entirely on a machine is the wrong approach. I'm no fan of UAC, but it is the world Microsoft wants us to live in. None of them really get rid of the UAC prompt, they just get the user's permission at some more convenient point in the launch process. (I realize not all of these apply to your situation:) Number one recommendation: avoid tasks that Microsoft considers "dangerous". Sometimes elevation is unavoidable but other times it is the result of using deprecated Windows programming techniques like writing a config file to the %ProgramFiles% directory. In these cases I recommend capitulation to the Borg; if Microsoft wants you to write to %AllUserProfile% instead of %ProgramFiles%, just give in and change it. I recommend testing the application in a regular (non-Administrative) user to see where the problems are. If you just want to do a simple SysExec call and you don't mind getting the prompt (i.e. it needs to work, but it doesn't need to work unattended), you can use John Robbins' elevate.exe to run the command (sc start or whatever) with elevation. We use elevate.exe in our installer batch file; the user clicks through one UAC prompt which elevates the batch file that runs multiple installers. (Johannes Passing wrote a version of elevate.exe in regular C; I haven't tried it.) Run your application as a service. Microsoft believes that applications that run unattended should be services. A service running in a privileged account (i.e. LocalSystem) can perform tasks that require elevation without any dialog boxes. The thought here is that since UAC elevation is required to install the service no further prompts are needed. This is how our production application runs. We use FireDaemon to do this and it is quite easy to convert your LV app to a service this way. (There is also the free "srvany.exe" application from Microsoft floating around.) There are other advantages to running as a service, such as automatic restarts when the application quits or crashes. There is, unfortunately, a winkle running an app as a service under Windows 7: starting with Vista, Microsoft no longer allows services to have user interfaces. (This is another security "feature".) Thus to go this route you will have split the functional part of your application from your UI. It's quite doable (especially if you use the VI Server) but if your application has a lot of UI this might be an untenable amount of work. I suppose you could go the other way and have a lightweight helper service that performs just a few tasks that require elevation under the command of your main app. That would be pretty easy. Have your application request elevation on startup. This is the method Microsoft wants you to take. (Well, not really: they want you to have buttons all over the place with little shield logos that only request elevation on the specific tasks that really require elevation.) You can request elevation by embedding a manifest in the application. This is supported by the LV Application Builder, although documentation is extremely sparse. See this PowerPoint presentation on how to construct the manifest. While you are at it, digitally signing the application will avoid another bunch of UAC warnings on installation and launch. You can use self-signed certificate or a "real" certificate backed up by Verisign etc. "Real" certificates cost money but provide traceability. Signing your code shows that your application hasn't been modified since your released it. While we are talking Windows 7, keep in mind that the Program Files directory is now called Program Files (x86) when 32-bit LV is running on a 64-bit versions of windows. So make sure to clean out those hard-coded paths! -Rob
  2. 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
  3. I know this is an ancient thread, but for posterity I wanted to note that in LV 8.2 and later, you can use the regular LabView "variant to data" function to convert ActiveX timestamps to LabView timestamps.
  4. 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. -Rob
  5. Whoops, I had disabled asynchronous downloads while testing. With Async set to TRUE again, Timeout does work with WaitForResponse(). I'm using an I32.
  6. "Timeout" seems to be ignored by WaitForResponse(), at least with all of the data types I can think of stuffing in there.
  7. 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! blocking: non-blocking:
  8. 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.
  • Create New...

Important Information

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