smackay Posted July 26, 2011 Share Posted July 26, 2011 Hi, I am experiencing difficulty using external dll functions. I think I am loading the function prototype correctly from ADVAPI32.DLL but when I use the "RegOpenKeyEx" function prototype with parameters I get the following "C1000 Channel or function not found: Main Line 50 - Uncaught error in sequence Main". What am I doing wrong? Attached is my CTL file. Also attached is some working VB6 code that uses ADVAPI32.DLL and develops a list of active Com ports. How can I do the same to populate my DAQFactory Combo Box? Thank you. Desktop.zip Link to comment Share on other sites More sharing options...
AzeoTech Posted July 26, 2011 Share Posted July 26, 2011 Before we get to far into this, why not just use ConfigureComm() to display the default DAQFactory comm configuration window? This doesn't enumerate the comm ports, but gives you all the comm parameters. Your code doesn't work because you specified the DAQFactory function name as Var.temp1, var.temp2, etc. It should be a string, and most likely "RegOpenKeyEx". Note that your VB code does the same thing with the Alias parameter. Link to comment Share on other sites More sharing options...
smackay Posted July 27, 2011 Author Share Posted July 27, 2011 Hi, Thank you for your response. I am still having difficulty with this extern DLL function. I have an auto baud detection routine that will check through available com ports offered by windows. I need to get this extern function call using the ADVAPI32.DLL to work. The ConfigureComm() does not connect to actual available com ports. I am trying to eliminate the need to go to device manager. I would like to have this list available in DAQFactory and then utilize ConfigureComm() options. This DLL also would give me access to other registry variables and functions aside from just the DAQFactory registry variables. Changes I changed the Var.Temp1 to "RegOpenKeyEx", in the "extern function", as you had suggested. You mentioned this was akin to Alias "RegEnumValueA" found in the VB code. I added "Global string RegOpenKeyEx" global declaration above the "extern function". I removed "Var." from the last parameter of the "RegOpenKeyEx" function call and left it as "lngKeyHandle". I placed "Global lngKeyHandle = 0" a "0" initialized global declaration above the "RegOpenKeyEx" function call. Problems "Var.lngResult" reports a null as the return value of the "RegOpenKeyEx" function call. "lngKeyHandle" does not contain a handle number other than the initialized "0". Questions Could you please help me to get this to work? The third parameter of the "RegOpenKeyEx" function call contains NULL which I used as a null pointer. In the VB code 0& was used. Is it correct to use NULL as a null pointer in that function call argument? Attached is the new CTL file with the modifications. Please reference the VB6 code sent last time. Thank you, Shawn Extern_Dll_for_Com_List.ctl Link to comment Share on other sites More sharing options...
AzeoTech Posted July 27, 2011 Share Posted July 27, 2011 1) you don't want to declare regOpenKeyEx as a variable. Its a function that is created by the extern command. 2) avoid using Var. notation. Its deprecated as of about 5 years ago. 3) NULL in C (i.e. the DLL) is different than null in DAQFactory. In DAQFactory it means the variable has no value. In C NULL is the same as 0. Link to comment Share on other sites More sharing options...
smackay Posted July 27, 2011 Author Share Posted July 27, 2011 I replaced the DAQFactory "NULL" in the RegOpenKeyEx function call with a for C based APIs. It did not make a difference that I could tell. I am still not returning a function value and the "lngKeyHandle" is still empty. How come VB uses a null pointer (0&) instead of a zero and works? Please help me make this work. Thanks Link to comment Share on other sites More sharing options...
AzeoTech Posted July 27, 2011 Share Posted July 27, 2011 A null pointer and 0 are the same thing. Its just how they are type cast. Your prototype is wrong. You have: extern("ADVAPI32.DLL", "Long RegOpenKeyEx(Long, String, Long, Long, Long)", "RegOpenKeyEx", "stdcall") but the last parameter is a pointer (it doesn't have "byVal" in the VB prototype). The other problem is that RegOpenKeyEx actually doesn't exist in ADVAPI32.DLL. You need to pick either the ANSI or wide character version, probably the ANSI version. The correct, working prototype is: extern("ADVAPI32.DLL", "Long RegOpenKeyExA(Long, String, Long, Long, Long[1])", "RegOpenKeyEx", "stdcall") This is actually true of many Windows functions. You don't see it in VB/VC so much because the compiler handles it through the header files, which map RegOpenKeyEx to one of the two functions based on whether you have the wide character precompiler flag set. Since you are accessing the DLL directly, and not through the headers, you have to know this on your own. How would you know? Two choices: 1) look at the header file. Unless you have visual C installed, you probably don't have access to it. 2) download "Depends" off the web and run in on the DLL. This program will list all the externally accessible functions in a DLL. Finally, since the last parameter is a pointer, you need to pass a reference to your variable, not the variable itself, so the function call is: private lngResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\DEVICEMAP\SERIALCOMM", 0, KEY_READ, @lngKeyHandle) Link to comment Share on other sites More sharing options...
smackay Posted July 29, 2011 Author Share Posted July 29, 2011 Thank you for all the information. I downloaded "Dependency Walker" and I see all the function names within ADVAPI32.DLL. I am still having problems with the suggested statements: extern("ADVAPI32.DLL", "Long RegOpenKeyExA(Long, String, Long, Long, Long[1])", "RegOpenKeyEx", "stdcall") private lngResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\DEVICEMAP\SERIALCOMM", 0, KEY_READ, @lngKeyHandle) Problems/Questions The problem is "lngResult = 6". The return value for "RegOpenKey" should be zero so that MsgBox "Cannot open key" does not surface. If you place a "Watch" on "@lngKeyHandle" nothing is available and if you watch lngKeyHandle contains the initized value of zero. Are enumaration statements correct?extern("ADVAPI32.DLL", "Long RegEnumValueA(Long, Long, String, Long[1], Long, Long[1], Long[1], Long[1])", "Long RegEnumValue", "stdcall") lngResult = Long RegEnumValue(lngKeyHandle, lngCurIdx, strValNm, @lngValNmLen, 0, @REG_SZ, @strValDt, @lngValDtLen) The "RegCloseKey" function does not have an Alias. Are the close key statements correct? extern("ADVAPI32.DLL", "Long RegCloseKey(Long)", "RegCloseKey", "stdcall") lngResult = RegCloseKey(lngKeyHandle) I have attached the CTL file. Thank you Extern_Dll_for_Com_List.ctl Link to comment Share on other sites More sharing options...
AzeoTech Posted July 30, 2011 Share Posted July 30, 2011 1) I can't say. How that function works is outside what I can really help you with since its a Windows function and not really anything to do with DAQFactory other than the fact that you are calling it from within DAQFactory. Most likely you have a parameter wrong. 2) @ should only be used when passing values to functions brought in by extern() or with some device drivers (the LabJack one in particular). Pointers are not supported in DAQFactory in general, so @ can't be used elsewhere. It wouldn't mean anything to you anyway, as @lngKeyHandle is just an address in memory. 3) only if the value you are expecting is a Long. lpData is an uncast pointer, meaning that you need to know what the function is going to put there first. In fact, one would typically call this function twice, once with NULL (0) for this parameter, in which case the function returns the length of data, then your program would allocate the appropriate amount of space, then you'd call it passing that allocated space. Unfortunately, DAQFactory doesn't quite give you that sort of flexibility. Instead, you'd have to figure out what the biggest possible value would be, then set the prototype to cover it, probably using BYTE[1000] or something. I suppose you could call extern() again to reset the prototype. Anyhow, if you leave it as Long[1] and you call something that returns more than 4 bytes, you are likely going to crash DAQFactory as the function overwrites memory it shouldn't. Alas, it might not crash every time, only once you send the app to your customer, thus is the nature of pointer bugs. 4) Not being a VB programmer, I was wrong about Alias. Alias appears to actually be the name of the function as it appears in the DLL. That's why the first two have the "A" at the end. The non-A form is the name that is used in VB. RegCloseKey, since it doesn't take any string parameters, doesn't need an A and W form, so the DLL name is probably just RegCloseKey. To explain A vs W: A is for standard, single byte characters, i.e. where each character is a number between 0 and 255. W is for wide, two byte character sets, such as Unicode, where each character is two bytes. OK, now being blunt: it is my recommendation that you consider alternatives based on your experience in programming in C. Doing more advanced DLL access through extern() is dangerous, and because of the way the enum works, I would qualify this is more advanced. If you were doing it in VB or VC, the compiler type checks for you based on the headers and thus prevents many errors. This doesn't happen in DAQFactory (or any time you truly dynamically link to a DLL). It is my recommendation that you instead code up in VB or VC, your choice, a function that enumerates the serial ports then writes the result to disk. Expose a the function in a DLL so you can call it from DAQFactory, or simply leave it as a standalone executable and run it using System.ShellExecute(). Then use the File. functions to read the file your external application created to get a list of serial ports on the machine. Using ShellExecute() to trigger an external application and files to read back results is much easier and more forgiving than doing it with a DLL. The shellExecute()/File method is, however, quite a bit slower, execution wise, and can't be used for anything more complex, but for simply enumerating the comm ports, which you likely will do only once, this is much better. Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.