Jump to content

BrowseForFolder Open Only Folder Labview


ano

Recommended Posts

Hi there, I am trying to make the Browse.vi (http://forums.ni.com/ni/board/message?board.id=170&thread.id=15938&view=by_date_ascending&page=1) working. There is such a thread in this forum too (), that is why I am posting here (In LAVA Lounge). Although it is good explain and the vi works, it lacks the example where one can use custom file dialog. I am using Windows 2000 and Labview 8.20. I tried to change lpszTitle as string, as uint8, as cluster of uint8 ... still the file dialog is not displayed properly. Do you have any idea why or some solution that one can browse only folders in a popup window?

Browse_Folders_002.vi

post-16492-125397571296_thumb.gif

Edited by ano
Link to comment

Hi there, I am trying to make the Browse.vi (http://forums.ni.com/ni/board/message?board.id=170&thread.id=15938&view=by_date_ascending&page=1) working. There is such a thread in this forum too (), that is why I am posting here (In LAVA Lounge). Although it is good explain and the vi works, it lacks the example where one can use custom file dialog. I am using Windows 2000 and Labview 8.20. I tried to change lpszTitle as string, as uint8, as cluster of uint8 ... still the file dialog is not displayed properly. Do you have any idea why or some solution that one can browse only folders in a popup window?

You can not pass LabVIEW clusters containing variable sized elements such as strings or arrays to a C function. LabVIEW stores its arrays (and strings which are in fact just byte arrays too) very differently than what a C API function expects. So the best apparoach is usually to write a wrapper DLL with a function that takes the elements in the cluster as individual parameters and constructs the C structure to pass to the real API.

There is in principle also a way to do it all in LabVIEW, but to be able to do that you need to know a lot more about C programming and its datatypes than you will need to write a wrapper DLL in C.

Rolf Kalbermatter

  • Like 2
Link to comment
You can not pass LabVIEW clusters containing variable sized elementssuch as strings or arrays to a C function. LabVIEW stores its arrays(and strings which are in fact just byte arrays too) very differentlythan what a C API function expects. So the best apparoach is usually towrite a wrapper DLL with a function that takes the elements in thecluster as individual parameters and constructs the C structure to passto the real API.

There is in principle also a way to do it all in LabVIEW, but to beable to do that you need to know a lot more about C programming and itsdatatypes than you will need to write a wrapper DLL in C.

Rolf Kalbermatter

Many thanks for the fast reply first. I know some c and all the datatypes. I have actually written a c structure in labview, that expects a character array (but not const pointer to char array). As far as I know a string is just a character array and a character is uint8 (unsigned int 8 bit) that represents an ascii number from the ascii table. So as I mentioned in my first post i tried to get the file dialog using a cluster with unit8 elements, but instead of normal text I still get some random characters as you can see on the pic in first post. I will attach these example too, so you can see yourself. Here http://msdn.microsof.../bb773205(en-us,VS.85).aspx the lpszTitle is defined as

Pointer to a null-terminated string that is displayed above the treeview control in the dialog box. This string can be used to specifyinstructions to the user. and the struct looks like this

typedef struct _browseinfo {

HWND hwndOwner;

PCIDLIST_ABSOLUTE pidlRoot;

LPTSTR pszDisplayName;

LPCTSTR lpszTitle;

UINT ulFlags;

BFFCALLBACK lpfn;

LPARAM lParam;

int iImage;

} BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO;

.

So here lpszTitle is dclared as LPCTSTR and LPCTSTR is "A 32-bit pointer to a null-terminated string of 8-bit Windows (ANSI) characters." from http://msdn.microsof...y/aa505945.aspx. So do I need a pointer to charachter array (const char*) and how do I do this. I tried with references to uint8[] and to a cluster, but it fails too.

Browse_Folders_003.vi

Browse_Folders_004.vi

Edited by ano
Link to comment

Many thanks for the fast reply first. I know some c and all the datatypes. I have actually written a c structure in labview, that expects a character array (but not const pointer to char array). As far as I know a string is just a character array and a character is uint8 (unsigned int 8 bit) that represents an ascii number from the ascii table. So as I mentioned in my first post i tried to get the file dialog using a cluster with unit8 elements, but instead of normal text I still get some random characters as you can see on the pic in first post. I will attach these example too, so you can see yourself. Here http://msdn.microsof.../bb773205(en-us,VS.85).aspx the lpszTitle is defined as .

So here lpszTitle is dclared as LPCTSTR and LPCTSTR is "A 32-bit pointer to a null-terminated string of 8-bit Windows (ANSI) characters." from http://msdn.microsof...y/aa505945.aspx. So do I need a pointer to charachter array (const char*) and how do I do this. I tried with references to uint8[] and to a cluster, but it fails too.

Look, a LabVIEW byte array (string) is like this:

typedef struct {

int32 len;

uInt8 elm[];

} **LStrHandle;

So you hav a number of problems to pass this as a C String pointer.

1) The actual character array starts with an offset into the real memory area.

2) The LabVIEW string is a handle (pointer to pointer)

3) Last but not least LabVIEW data can be "hyperdynamic".

This last one is very important! LabVIEW reserves the right to manage its memory in anyway that makes sense to it. So if you create a string containing a set of characters, then run some magic like memcpy() (I prefer the LabVIEW manger call MoveBlock() for this) through a Call Library Node to get at the pointer that points to the actual string and try to pass that pointer to another Call Library Node to your API, LabVIEW might have reused, resized, or deallocated the original string already at the time your API is called. This in the best case could mean a simple Access Violation or crash or if you are unlucky a much less noticeable effect such as strange interactions with other parts of your code that operate with the reused memory, where your API now tries to write something into (or the other function reusing that memory writes something in and your API reads suddenly gibberish).

There are tricks such as making sure the original string wire runs through the diagram without any branching to some place that due to dataflow dependency will execute after the call to your CLN API, to hopefully make LabVIEW retain that string memory until after your API executes. However while this did work in older versions, there is always the chance that by new and improved optimization strategies in newer LabVIEW versions, this suddenly might fail. The reason this works until now is that LabVIEW does not usually do diagram optimizations across structure boundaries, so if you run your string wire to a sequence structure border that depends on your CLN being called and finished too, you are nowadays safe. But if you just wire that string wire to the border without using it, there is a good chance that a newer LabVIEW version with an improved optimization algorithme might realize that this string is never used after your memcpy() hokuspokus call and suddenly the string is gone anyhow, at the time your API call tries to access the pointer that you retrieved with so much virtue from the LabVIEW string handle.

Rolf Kalbermatter

  • Like 2
Link to comment
Look, a LabVIEW byte array (string) is like this:

.....

Thanks again for the great advice. I see what you mean now. I will have to search some info, about the wrapper dll, I presume that google is my friend :D. Then I will see what I can do. Any hints would be greatly appreciated.

Cheers

Link to comment
try this

/dirk

Hi I can not open the file, because I am using LV 8.20 and it says it is for 8.5. Can you compile it for 8.20?

P.S: I am installing now cygwin and mingw and I will try to write the wrapper dll. I found the "Call DLL.vi" example where it is shown how the function in the dll should look like.

If I succeed I will post the result here.

Cheers

Ano

Link to comment

Ano,

I don't have 8.2 installed anymore (and if you have any chance to upgrade to 9.0 you should :) so I haven't been able to test it.

But here you go. No wrapper DLL's involved.

Hi I can not open the file, because I am using LV 8.20 and it says it is for 8.5. Can you compile it for 8.20?

P.S: I am installing now cygwin and mingw and I will try to write the wrapper dll. I found the "Call DLL.vi" example where it is shown how the function in the dll should look like.

If I succeed I will post the result here.

Cheers

Ano

BrowseForFolderG.vi

  • Like 1
Link to comment
Ano,

I don't have 8.2 installed anymore (and if you have any chance to upgrade to 9.0 you should :) so I haven't been able to test it.

But here you go. No wrapper DLL's involved.

Many thanks Dirk, really appreciate. It is really nice solution with these two functions void MoveBlock(const CStr src, long dst, long size); and long AZNewPtr(long size);. My installation of LabView 8.20 on windows 2000 did not find the void ILFree(long PointerToIDList); function, but i just deleted it and it works perfectly. It may not clean the allocated space at the moment, but hey assuming it runs on a windows os the pc is restarted from time to time : ). About the upgrade, I am programming in one company and because the upgrade costs money they ask me "Ok is it working with the version that we have?" and I say "Yes it is." and they "Then why should we upgrade?". And I know all the advantages, bug fixes, new functions .. but go explain.

Link to comment

Hm strange ms says " Note When using Microsoft Windows 2000 or later, use CoTaskMemFree rather than ILFree. ITEMIDLIST structures are always allocated with the Component Object Model (COM) task allocator on those platforms.", but CoTaskMemFree is also not in shell32.dll.

P.S. Ok again too fast. CoTaskMemFree is in ole32.dll ..

Edited by ano
Link to comment

Hm strange ms says " Note When using Microsoft Windows 2000 or later, use CoTaskMemFree rather than ILFree. ITEMIDLIST structures are always allocated with the Component Object Model (COM) task allocator on those platforms.", but CoTaskMemFree is also not in shell32.dll.

P.S. Ok again too fast. CoTaskMemFree is in ole32.dll ..

ILFree() was an undocumented shell32 function since Win95 and Microsoft documented it after the big monopilist case they got into, which was I believe after Win 2000 was released. Those undocumented functions were exported by shell32 but not by name but only by ordinal instead. The ordinal number for ILFree is 155.

And now the nice part: The Call Library node supports importing by ordinal. Just enter the number into the library name field.

PS: ILFree is indeed just a wrapper around CoTaskMemFree even on older platforms.

Rolf Kalbermatter

  • Like 1
Link to comment
ILFree() was an undocumented shell32 function since Win95 and Microsoft documented it after the big monopilist case they got into, which was I believe after Win 2000 was released. Those undocumented functions were exported by shell32 but not by name but only by ordinal instead. The ordinal number for ILFree is 155.

And now the nice part: The Call Library node supports importing by ordinal. Just enter the number into the library name field.

PS: ILFree is indeed just a wrapper around CoTaskMemFree even on older platforms.

Rolf Kalbermatter

Wow ..... interesting!!

Link to comment

version for LabView 8.20 ..

Wow very nice, I always just use the built in LabVIEW browse for folder, but it's not always intuitive for people new to LabVIEW applications. I played around a bit with your vi and it acted strange when I changed the root to anything other than Desktop.

If I made root Fonts for example and ran it, the vi would run and complete but no dialog would come up, then LabVIEW wouldn't allow me to run it again, or change any other controls, I am forced to restart LabVIEW. Running 8.20, or 8.6 in XP SP3.

Link to comment
Wow very nice, I always just use the built in LabVIEW browse for folder, but it's not always intuitive for people new to LabVIEW applications. I played around a bit with your vi and it acted strange when I changed the root to anything other than Desktop.

If I made root Fonts for example and ran it, the vi would run and complete but no dialog would come up, then LabVIEW wouldn't allow me to run it again, or change any other controls, I am forced to restart LabVIEW. Running 8.20, or 8.6 in XP SP3.

Hi Hooovahh,

I did not notice at first that that you now say. You are right I get the errors too, when I change the root path. The vi is not mine, Dirk uploaded it kindly (and actually saved my day) and I just extended it a bit so to say, that is why I do not have straight solution or answer to this bug. I will see if I can do smth. Thanks for the feedback.

Edited by ano
Link to comment

Nope didn't see that it works fine thanks. Any idea why NI doesn't make a better native browse for folder dialog?

Because this Browse for Folder is really useless on older Windows platforms. Before XP it is simply a non-resizable mini dialog that is no use at all for serious Browse for Folder work. Also because anything like this they do on Windows they have to port somehow to Linux and Mac too. Mac probably has such a thing but I doubt you find such a native dialog specifically for Folders on Linux.

And why it doesn't work with anything but the desktop for the PIDL is logical. A PIDL is not an enum but a binary data structure with private content that describes a path in terms of the shell name space. If you select desktop this is NULL so BrowseForFolder interprets this as a NULL PIDL which happens to be the Desktop too. But in all other cases it interprets the value as a pointer (oops pointer to address 1 is a surprise that it didn't immediately crash).

To create PIDLs you have specific shell32 APIs that take a path and return the according PIDL to it such as SHParseDisplayName(). Or you could use SHGetSpecialFolderLocation() which probably could translate your enum directly into a pidl, though I didn't check if the CSIDL enum of that function matches with your enum.

Rolf Kalbermatter

Link to comment

I've made my own version of the folder browsing VI. I figured out a way to pass in an arbitrary root folder, so the browsing window only displays the folder and all its subfolders. (This VI is in LV 8.5)

There is most likely an issue with the way you allocate the pidl. In fact you should not allocate it as SHParseDisplayName() will do that for you and return the pointer. Allocating a pointer and telling LabVIEW that you pass a pointer to an array is, well .... strange and not right at all.

And at the end you want to deallocate that pidl with ILFree().

Rolf Kalbermatter

Link to comment

There is most likely an issue with the way you allocate the pidl. In fact you should not allocate it as SHParseDisplayName() will do that for you and return the pointer. Allocating a pointer and telling LabVIEW that you pass a pointer to an array is, well .... strange and not right at all.

And at the end you want to deallocate that pidl with ILFree().

Rolf Kalbermatter

You're right about the SHParseDisplayName(). I'm not sure even why I wrote it the way I did. As for the parameter specification for the pidl, I have absolutely no idea how to specify a pointer to an arbitrary structure in the Call Library node. The LabView documentation gives no examples, so I just took my best guess and figured that as long as I was telling it that I was specifying a pointer, that was good enough. I only specified Pointer to Array because the other two options didn't seem correct. I figured that I would just treat the structure as a byte array. If that is incorrect, then what should I be using for that parameter?

Link to comment

You're right about the SHParseDisplayName(). I'm not sure even why I wrote it the way I did. As for the parameter specification for the pidl, I have absolutely no idea how to specify a pointer to an arbitrary structure in the Call Library node. The LabView documentation gives no examples, so I just took my best guess and figured that as long as I was telling it that I was specifying a pointer, that was good enough. I only specified Pointer to Array because the other two options didn't seem correct. I figured that I would just treat the structure as a byte array. If that is incorrect, then what should I be using for that parameter?

A pointer is simply a 32bit integer. And in LabVIEW 8.6 and better a pointer sized integer, as far as the Call Library Node is concerned. So the ppidl parameter of SHParseDisplayName() would be a pointer sized integer passed by reference.

Rolf Kalbermatter

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

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