Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


dadreamer last won the day on October 17

dadreamer had the most liked content!


Profile Information

  • Gender

LabVIEW Information

  • Version
    LabVIEW 2023
  • Since

Recent Profile Visitors

3,110 profile views

dadreamer's Achievements

Community Regular

Community Regular (8/14)

  • Very Popular Rare
  • Conversation Starter Rare
  • Reacting Well Rare
  • First Post Rare
  • Collaborator Rare

Recent Badges



  1. I have seen LabWindows 2.1 on old-dos website. Don't know if it's of any interest for you tho'. As to BridgeVIEW's, I still didn't find anything, neither the scene release nor the customer distro. Seems to be very rare. Sure some collectors out there would appreciate, if you archive them somewhere on the Wayback Machine. 🙂
  2. It's utilizing the PCRE library, that is incorporated into the code. It's a first incarnation of PCRE, 8.35 for a 32-bit lvserial.dll and 8.45 for a 64-bit one. When configuring the serial port, you can choose between four variants of the termination: /* CommTermination2 * * Configures the termiation characters for the serial port. * * parameter * hComm serial port handle * lTerminationMode specifies the termination mode, this can be one of the * following value: * COMM_TERM_NONE no termination * COMM_TERM_CHAR one or more termination characters * COMM_TERM_STRING a single termination string * COMM_TERM_REGEX a regular expression * pcTermination buffer containing the termination characters or string * lNumberOfTermChar number of characters in pcTermination * * return * error code */ Now when you read the data from the port (lvCommRead -> CommRead function), it works this way: //if any of the termination modes are enabled, we should take care //of that. Otherwise, we can issue a single read operation (see below) if (pComm->lTeminationMode != COMM_TERM_NONE) { //Read one character after each other and test for termination. //So for each of these read operation we have to recalculate the //remaining total timeout. Finish = clock() + pComm->ulReadTotalTimeoutMultiplier*ulBytesToRead + pComm->ulReadTotalTimeoutConstant; //nothing received: initialize fTermReceived flag to false fTermReceived = FALSE; //read one byte after each other and test the termination //condition. This continues until the termination condition //matches, the maximum number bytes are received or an if //error occurred. do { //only for this iteration: no bytes received. ulBytesRead = 0; //calculate the remaining time out ulRemainingTime = Finish - clock(); //read one byte from the serial port lFnkRes = __CommRead( pComm, pcBuffer+ulTotalBytesRead, 1, &ulBytesRead, osReader, ulRemainingTime); //if we received a byte, we shold update the total number of //received bytes and test the termination condition. if (ulBytesRead > 0) { //update the total number of received bytes ulTotalBytesRead += ulBytesRead; //test the termination condition switch (pComm->lTeminationMode) { case COMM_TERM_CHAR: //one or more termination characters //search the received character in the buffer of the //termination characters. fTermReceived = memchr( pComm->pcTermination, *(pcBuffer+ulTotalBytesRead-1), pComm->lNumberOfTermChar) != NULL; break; case COMM_TERM_STRING: //termination string //there must be at least the number of bytes of //the termination string. if (ulTotalBytesRead >= (unsigned long)pComm->lNumberOfTermChar) { //we only test the last bytes of the receive buffer fTermReceived = memcmp( pcBuffer + ulTotalBytesRead - pComm->lNumberOfTermChar, pComm->pcTermination, pComm->lNumberOfTermChar) == 0; } break; case COMM_TERM_REGEX: //regular expression //execute the precompiled regular expression fTermReceived = pcre_exec( pComm->RegEx, pComm->RegExExtra, pcBuffer, ulTotalBytesRead, 0, PCRE_NOTEMPTY, aiOffsets, 3) >= 0; break; default: //huh ... unknown termination mode _ASSERT(0); fTermReceived = 1; } } //Repeat this until // - an error occurred or // - the termination condition is true or // - we timed out } while (!lFnkRes && !fTermReceived && ulTotalBytesRead < ulBytesToRead && Finish > clock()); //adjust the result code according to the result of //the read operation. if (lFnkRes == COMM_SUCCESS) { if (!fTermReceived) { //termination condition not matched, so we test, if the max //number of bytes are received. if (ulTotalBytesRead == ulBytesToRead) lFnkRes = COMM_WARN_NYBTES; else lFnkRes = COMM_ERR_TERMCHAR; } else //termination condition matched lFnkRes = COMM_WARN_TERMCHAR; } } else { //The termination is not activated. So we can read all //requested bytes in a single step. lFnkRes = __CommRead( pComm, pcBuffer, ulBytesToRead, &ulTotalBytesRead, osReader, pComm->ulReadTotalTimeoutMultiplier*ulBytesToRead + pComm->ulReadTotalTimeoutConstant ); } As shown in the code above, when the termination is activated, the library reads data one byte at a time in a do ... while loop and tests it against the term char / string / regular expression on every iteration. I can't say how good that PCRE engine is as I never really used it.
  3. And what's the image data type (U8, U16, RGB U32, ...)? You need to know this as well to calculate the buffer size to receive the image into. Now, I assume, you first call the CaptureScreenshot function and get the image pointer, width and height. Second, you allocate the array of proper size and call MoveBlock function - take a look at Dereferencing Pointers from C/C++ DLLs in LabVIEW ("Special Case: Dereferencing Arrays" section). If everything is done right, your array will have data and you can do further processing.
  4. There's nothing special about these two VIs. They are just helpers for the higher level examples. You don't need to run them directly. In real life projects you won't need those VIs at all. What about taking some image processing and machine vision courses?
  5. The whole examples folder from VDM for LabVIEW 2010. examples.rar Couldn't find any VI with the same or similar name.
  6. As a workaround, what about using the .NET control's own events? Mouse Event over .NET Controls.vi MouseDown CB.vi MouseMove CB.vi
  7. Technically related question: Insert bytes into middle of a file (in windows filesystem) without reading entire file (using File Allocation Table)? (Or closer, but not that informative). The extract is - theoretically possible, but so low level and hacky that easy to mess up with something, rendering the whole system inoperable. If this doesn't stop you, then you may try contacting Joakim Schicht, as he has made a bunch of NTFS tools incl. PowerMft for low level modifications and maybe he will give you some tips about how to proceed (or give it up and switch to traditional ways/workarounds).
  8. It is what I was thinking of, just in case with Memory-Mapped Files it should be a way more productive, than with normal file operations. No need to load entire file into RAM. I have a machine with 8 GB of RAM and 8 GB files are mmap'ed just fine. So, general sequence is that: Open a file (with CreateFileA or as shown above) -> Map it into memory -> Move the data in chunks with Read-Write operations -> Unmap the file -> SetFilePointer(Ex) -> SetEndOfFile -> Close the file.
  9. Not an issue for "100kB" views, I think. Files theirselves may be big enough, 7.40 GB opened fine (just checked).
  10. I would suggest Memory-Mapped Files, but I'm a bit unsure whether all-ready instruments exist for such a task. There's @Rolf Kalbermatter's adaptation: https://forums.ni.com/t5/LabVIEW/Problem-Creating-File-Mapping-Object-in-Memory-Mapped-FIles/m-p/3753032#M1056761 But seems to need some tweaks to work with common files instead of file mapping objects. Not that hard to do though. A quick-n-dirty sample (reading 10 bytes only). Yes, I know I should use CreateFileA instead of Open/Create/Replace VI + FRefNumToFD, just was lazy and short on time.
  11. Not sure why this is still unanswered, but there is a bunch of similar threads, when googling something like string color array site:ni.com. The main verdict is "not possible, use Table/Listbox, Luke" (or any other suitable workaround of your choice).
  12. As I see, ww257x_32.dll statically depends on the following: TE5351.dll TeViEnet.dll cvirt.dll The first two are in the same "WW-257X IVI Driver 1.1.14" archive and for the latter you may try installing LabWindows/CVI Runtime (Legacy) or LabWindows/CVI Runtime for 8.5 version as suggested on the driver page. Besides of that both TE5351.dll and TeViEnet.dll depend on VISA32.dll, so you should have NI-VISA 4.6 (or above) installed too. Plus there's IVI Compliance Package 3.2 requirement.
  13. Usual disclaimer. Method described below is strictly experimental and not recommended to use in real production. N.B. This is based on .NET, therefore Windows only. This text is sorta lengthy, but no good TL;DR was invented. You may scroll down to the example, if you don't want to read it all. One day I was stalking around NI forums and looking at how folks implement their callback libraries to call them from LabVIEW. After some time I came across something interesting: How to deal with the callback when I invoke a C++ dll using CLF? There someone has figured out how to make LabVIEW give us a .NET delegate using a dummy event. This technique is different from classic way of interfacing to callbacks, because it allows to implement the callback logic inside a VI (not inside a DLL), but still it requires writing some small assembly to export the event. Even though Rolf said there that it's not elegant, I decided to study these samples better. Well, it was, yeah, simple (no wonder it was called SimpleProxy/SimpleDemo/SimpleCallBack) and very instructive at the same time. It worked very well in both 32- and 64-bit LabVIEW, so I had fun to play around and learn some new things about .NET events and C#. After all I started to think, whether we really need this dummy assembly to obtain a delegate... Initially I was looking for a way to create a .NET event at the run-time with Reflection.Emit or with Expression Trees or somehow else, but after googling for few days and trying many things in both C# and F# I came to a conclusion that it's impossible. One just can create event handlers and attach them to already existing events, not create events on their own. Okay. First I decided to know how exactly LV native Register Event Callback node works. Looking ahead I'll say it was a dead end, but interesting. Ok, the Register Event Callback node in fact consists of two internal functions - DynEventAllocRegInfo and DynEventRegister - with the RegInfo structure filling between them. The first one creates and returns a new RegInfo with a Reg Event Callback reference, the second one actually registers the RegInfo and the reference in the VI Data Space (the prototypes and the struct fields are more or less figured out). But when I started to play with the CLFN's to replace the Register Event Callback node, I ran into few pitfalls. To work properly the DynEventRegister function needs one of the RegInfo's fields to be a type index of (hidden) upper left terminal of the Constructor node. This index is stored in the VI's Data Space Type Map (DSTM) and determined at the compile time. I did not find a reliable way to pull it out of the DSTM. Moreover the RegInfo struct doesn't have a field for the VI Entry Point or anything like that. Instead LabVIEW stores the EP in some internal tables and it's rather complicated to get it from there. For these reasons I have given up studying the Register Event Callback node. Second I turned my attention to the delegate call by its pointer. I soon found out that LabVIEW generates some middle layer (by means of .NET) to convert the parameters and other stuff of the native call to the VI call. That conversion was performed by NationalInstruments.LabVIEW###.dll in the resource folder (a hidden gem!). This assembly has almost everything that we need: the CallbackInfo and CallbackHandler classes, and the latter has two nice methods: CallLabView and CreateCallbackHandlerDelegate. Referring to the SimpleDemo/SimpleCallBack example, when we call the delegate by its pointer, LabVIEW calls this chain: CallLabView -> EventCallbackVICall internal function -> VI EP. All that was left to do was to try it on the diagram with .NET nodes, but... there was another obstacle. Sure that you'll connect the inputs right? You will not. These parameters are not what they seem (at least, one of them). The viref is not a VI reference, but a VI Entry Point pointer. It's not a classic function EP pointer, but a pointer to a LabVIEW internal struct, which eases the VI calls (it's called "Vepp" in the debug info). The userParam is a pointer to the User Parameter as for the Register Event Callback node. The cookie is a pointer to the .NET object refnum from the Constructor node (luckily NULL can be passed). And the type and flags are 0 and 0x80000000 for standard .NET callbacks. Now how and where could we get that VI EPP? Good question. There is a function inside LabVIEW, that receives a VI ref and returns an allocated VI EPP. But sadly it's not exported at all. Of course, this ain't stoppin' us. I used a technique to find the function by a string constant reference in the memory of a process. It's known to be not very reliable between different versions of the application, therefore many tests were made on many versions of LabVIEW. After finding the function address, it's possible to call it using this method (kind of a hack as well, so beware). Is this all enough to run .NET nodes now? For the CallLabView yes. It's simplier than CreateCallbackHandlerDelegate, but doesn't provide a delegate. It passes the parameters to the VI, calls it and returns. The return and parameters could be utilized onwards, of course, but nothing more. To obtain a delegate it's necessary to call the CreateCallbackHandlerDelegate. This method wants the handlerType input wired and to be valid in .NET terms, so proper type must be made. Initially I tried to use .NET native generic delegates: Action, Predicate and Func. Everything went fine except the GetFunctionPointerForDelegate, which didn't want to work with such delegates and complained. The solution was in applying some obscure MakeNewCustomDelegate method as proposed here. Now the GetFunctionPointerForDelegate was happy to provide a pointer to the delegate and I successfully called the callback VI both "manually" and by means of Windows API. So finally the troubles were over, so I could wrap everything into SubVI's and make a basic example. I chose EnumWindows function from WinAPI, because it's first that came to my mind (not the best choice as I think now). It's a simple function: it's called once with a callback pointer and then it starts looping through OS windows, calling a callback on each iteration and passing a HWND to it. This is top-level diagram of the example: I won't be showing the SubVI's diagrams here as they are rather bulky. You may take a look at them on your own. I'll make one exception though - this is the BD of the callback VI. As you could know, EnumWindowsProc function must return TRUE (1) to continue windows enumeration. How do we return something from a callback VI? Well, it's vaguely described here, I clearly focus on this. You must supply first parameter as a return value in both Event Data clusters and assign these two to the conpane. On the diagram you set the return as you need. These are the versions on which I tested this example (from top to bottom). Some nuances do exist, but generally everything works well. LabVIEW 2023 Q3 32 & 64 (IDE & RTE) LabVIEW 2022 Q3 32 & 64 (IDE & RTE) LabVIEW 2021 32 & 64 (IDE & RTE) LabVIEW 2020 32 & 64 (IDE & RTE) LabVIEW 2019 32 & 64 (IDE & RTE) // 32b - on one machine RTE worked only w/ "Allow future versions of the LabVIEW Runtime to run this application" disabled (?), 64b - OK LabVIEW 2018 32 & 64 (IDE & RTE) LabVIEW 2017 32 & 64 (IDE & RTE) // CallbackInfo& lvCbkInfo, not ptr LabVIEW 2016 32 & 64 (IDE & RTE) // same LabVIEW 2015 32 & 64 (IDE & RTE) // same LabVIEW 2014 32 & 64 (IDE & RTE) // same LabVIEW 2013 SP1 32 & 64 (IDE & RTE) // same + another string ref + 64b: "lea r8" (4C 8D 05) instead of "lea rdx" (48 8D 15) LabVIEW 2013 32 & 64 (IDE & RTE) // same + no ReleaseEntryPointForCallback, CreateCallbackHandler instead of CreateCallbackHandlerDelegate LabVIEW 2012 32 & 64 (IDE & RTE) // same + forced .NET 4.0 LabVIEW 2011 32 & 64 (IDE & RTE) // same LabVIEW 2010 32 & 64 (IDE & RTE) // same LabVIEW 2009 32 & 64 (IDE & RTE) // same EnumWindows (LV2013).rar EnumWindows (LV2009).rar How to run: Select the appropriate archive according to your LV version: for LV 2013 SP1 and above download "2013" archive, for LV 2009 to 2013 download "2009" archive. Open EnumWindows32.vi or EnumWindows64.vi according to the bitness of your LV. When opened LV will probably ask for NationalInstruments.LabVIEW###.dll location - point to it going to the resource folder of your LV. Next open Create Callback Handler Delegate.vi diagram (has a suitcase icon) and explicitly choose/load NationalInstruments.LabVIEW###.dll for these nodes marked red: It's only required once as long as you stay on the same LV version. For the constant it may be easier to create a fresh one with RMB click on the lvCbkInfo terminal and choosing "Create Constant" entry. Now save everything and you're ready to run the main VI. Remarks / cons: no magic wand for you as for the Register Event Callback node, you create a callback VI on your own; the parameters and their types must be in clear correspondence to those of the delegate; obviously .NET callbacks are X times slower than pure C/C++ (or any other unmanaged code) DLL; search for CreateVIEntryPoint function address takes time (about several seconds usually); on 64 bits it lasts longer due to indirect ref addressing; no good way to deallocate VI EPP's; the ReleaseEntryPointForCallback function destroys AppDomain when called (after such a call the VI must be reopened to get .NET working) - usually not a problem for EXE's. Conclusion. Although it's a kind of miracle to see a callback VI called from 'outside', I doubt I will use it anywhere except home. Besides its slowness it involves so many hacks on all possible levels (WinAPI, .NET, LabVIEW) that it's simply dangerous to push such an application to real life. Likely this thread is more of a detailed reference for the future idea in NI Idea Exchange section.
  14. Since LV 2021 hooking CfgGetDefault is not enough. There are two new classes in the mgcore library: LazyConfigValue_Bool32 and LazyConfigValue_PathRef. I assume, they're introduced for faster access to the token values. I always used good ol' WinAPIOverride by Jacquelin Potier to catch API calls (including LV ones), but now it seems that it lacks some necessary functionality (e.g., custom actions on a BP hit). So I decided to adapt that Lua script. LVEXEPath = "C:\\Program Files (x86)\\National Instruments\\LabVIEW 2023\\LabVIEW.exe" MGCore = "mgcore_SH_23_3.dll" FoundIt = false Tracing = false list = createStringlist() list.Sorted = true list.setDuplicates(dupIgnore) -- attach before any dynamically loaded modules, so break on EP (last arg) createProcess(LVEXEPath, "", true, true) CfgGetDefault = getAddress("LabVIEW.CfgGetDefault") debug_setBreakpoint(CfgGetDefault) LoadLibraryExA = getAddress("kernelbase.LoadLibraryExA") debug_setBreakpoint(LoadLibraryExA) function debugger_onBreakpoint() if EIP == CfgGetDefault then local tType = readString(ESP+4, 4) local size = readBytes(readInteger(ESP+8), 1, false) local token = readString(readInteger(ESP+8) + 1, size) local addr = readInteger(ESP+12) if (list.IndexOf(token) == -1) then list.add(token) print(string.format("%s (0x%X) [%s]", token, addr, tType)) end debug_continueFromBreakpoint(co_run) return 1 elseif EIP == LoadLibraryExA then local mod = readString(readInteger(ESP+4), 255) if (string.find(string.lower(mod),string.lower(MGCore))) then print("MGCore loaded") Tracing = true debug_continueFromBreakpoint(co_stepover) return 1 else debug_continueFromBreakpoint(co_run) return 1 end elseif EIP == mgc_f1 then --LazyConfigValue_Bool32::LazyConfigValue_Bool32 --print(getNameFromAddress(EIP)) local token = readString(readInteger(ESP+4), 50) if (list.IndexOf(token) == -1) then list.add(token) print(string.format("%s [Bool]", token)) end debug_continueFromBreakpoint(co_run) return 1 elseif EIP == mgc_f2 then --LazyConfigValue_PathRef::LazyConfigValue_PathRef --print(getNameFromAddress(EIP)) local token = readString(readInteger(ESP+4), 50) if (list.IndexOf(token) == -1) then list.add(token) print(string.format("%s [Path]", token)) end debug_continueFromBreakpoint(co_run) return 1 elseif EIP == mgc_f3 then --LazyConfigValue_Bool32::FindExposedValue --print(getNameFromAddress(EIP)) local token = readString(readInteger(ESP+4), 50) if (list.IndexOf(token) == -1) then list.add(token) print(string.format("%s [Bool]", token)) end debug_continueFromBreakpoint(co_run) return 1 elseif EIP == mgc_f4 then --LazyConfigValue_Bool32::operator bool --print(getNameFromAddress(EIP)) local token = readString(readInteger(ECX+4), 50) local tType = readString(ECX+8, 4) if (list.IndexOf(token) == -1) then list.add(token) print(string.format("%s (_) [%s]", token, tType)) end debug_continueFromBreakpoint(co_run) return 1 else if (Tracing) and (not FoundIt) then debug_continueFromBreakpoint(co_stepover) extra, opcode, bytes, addy = splitDisassembledString(disassemble(EIP)) RetFound = string.find(opcode, "ret") if RetFound then print(string.format("RET found as %s", opcode)) FoundIt = true Tracing = false debug_removeBreakpoint(LoadLibraryExA) reinitializeSymbolhandler(true) mgc_f1 = getAddress("mgcore_SH_23_3.LazyConfigValue_Bool32::LazyConfigValue_Bool32") debug_setBreakpoint(mgc_f1) mgc_f2 = getAddress("mgcore_SH_23_3.LazyConfigValue_PathRef::LazyConfigValue_PathRef") debug_setBreakpoint(mgc_f2) mgc_f3 = getAddress("mgcore_SH_23_3.LazyConfigValue_Bool32::FindExposedValue") debug_setBreakpoint(mgc_f3) -- ! USE WITH CAUTION -- This func is called THOUSANDS of times -- LV becomes lagging and badly responsive mgc_f4 = getAddress("mgcore_SH_23_3.LazyConfigValue_Bool32::operator bool") debug_setBreakpoint(mgc_f4) return 1 end else debug_continueFromBreakpoint(co_run) return 1 end end end Not that I'm a big fan of scripting languages, plus Lua in CE acts odd sometimes, so this script is far away from ideal. It also hooks only a few LazyConfigValue functions as the rest doesn't really matter. Now here's what I've got. Launching LabVIEW: Creating a new VI: Doing various stuff in there (incl. building an EXE as the last operation): No new tokens on the VI close or LabVIEW exit. Did I grab them all? Very unlikely. But I think, tokens for most common scenarios are on the list. And there are some interesting ones.
  15. I'd add here Rolf's excellent revelations on interfacing external code to LabVIEW External Code in LabVIEW, Part1: Historical Overview External Code in LabVIEW, Part2: Comparison between shared libraries and CINs External Code in LabVIEW, Part3: How to use the Call Library Node properly to ease multi platform support
  • Create New...

Important Information

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