Jump to content

Using the DLL files of an application compiled with C# with labview


Recommended Posts

2 hours ago, alvise said:

I can see the function names you mentioned here in HCNetSDK.dll.
So you're talking about the possibility of using them directly to receive video streams?

No! The function mentioned will simply return a single JPEG image frame (probably only if an acquisition is running). Still it is useful to get this working first to actually get the interface at least exercised. Before you can do a Snap with this function you can do whatever acrobatics you would like but it is useless. Start with easier things and get them working, then move on to play with the dangerous tools. 😀

Link to comment
8 hours ago, alvise said:

So you're talking about the possibility of using them directly to receive video streams?

As Rolf says. No!

They will give you chance to figure out how to call functions without everything crashing and what needs to be initialised before you call any functions. They are simple functions so a good starting point. You have to walk before you can run.

Link to comment

-I understand the situation a little more. JPEG can only be used for snapshots.
-I can compile the sample application now. We can make changes to the interface or other functions.

Now I have taken the next step.

Quote

 

Next. That example saves a file in the callback (实时流数据.ps). You may have to change the filename to an ascii one for LabVIEW to read it as LabVIEW doesn't support Unicode. You should be able to read that file from LabVIEW (after is is closed). That is the raw data that you would normally get back from a callback. Read that file in LabVIEW and figure out how to interpret it (it's a byte array [array of u8]. The callback is

public void RealDataCallBack(Int32 lRealHandle, UInt32 dwDataType, IntPtr pBuffer, UInt32 dwBufSize, IntPtr pUser)

This callback (for a .NET Assembly or a similar one for C[++]) would be where you put the PostLVUserEvent instead of saving to a file. Your problem at that point is telling the callback to use a LabVIEW refnum.  Don't worry about putting that in for now, we can talk about that later.

 

Quote

 

        public void RealDataCallBack(Int32 lRealHandle, UInt32 dwDataType, IntPtr pBuffer, UInt32 dwBufSize, IntPtr pUser)
        {
            if (dwBufSize > 0)
            {
                byte[] sData = new byte[dwBufSize];
                Marshal.Copy(pBuffer, sData, 0, (Int32)dwBufSize);

                string str = "实时流数据.ps";
                FileStream fs = new FileStream(str, FileMode.Create);
                int iLen = (int)dwBufSize;
                fs.Write(sData, 0, iLen);
                fs.Close();            
            }
        }

 

I think this is "实时流数据.ps." It's a PS (PostScript) extension, isn't it? I will change the name.

I'm trying to understand the situation by looking at the example here. But how can I adapt this to the code I'm using?

Link to comment
2 hours ago, alvise said:

I think this is "实时流数据.ps." It's a PS (PostScript) extension, isn't it? I will change the name.

I'm trying to understand the situation by looking at the example here. But how can I adapt this to the code I'm using?

Almost certainly not a PostScript file. That PS probably stands for something the developer ate that day or maybe it could be his initials or the initials of his girl friend. We likely will never know. 😀

Are you able to even receive the JPEG file yet? Remember what Shaun said. Try to walk before you run. And I would say getting the JPEG download work yourself without someone doing all the work for you is already advanced walking training. To get the Callback function to work is Marathon style running on Olympic levels. 😆

Edited by Rolf Kalbermatter
Link to comment
Quote

Almost certainly not a PostScript file. That PS probably stands for something the developer ate that day or maybe it could be his initials or the initials of his girl friend. We likely will never know. 😀

Something that comes up just when trying to guess is "PostScript".
 then I will add different letters there. OK? :)

Saves a photo with the attached code to the location where the labview code is located. Currently the code is bound to the event indexed button so it takes a picture and the program is stopped, when it is stopped it is recording a picture.

Should I go to the marathon right now? :)
I also want to pass, but I don't know exactly how. To use the PostLVUserEvent function, the extcode.h header file must be added to the C# code, but that is a separate event for how to do it in C#.

hikvision 7.0.1.vi

Link to comment
22 minutes ago, alvise said:

I also want to pass, but I don't know exactly how. To use the PostLVUserEvent function, the extcode.h header file must be added to the C# code, but that is a separate event for how to do it in C#.

You don't. C# does not understand C syntax and therefore the only thing that trying to add the extcode.h header file will achieve is to create compile errors.

If you want to access unmanaged APIs in C# you have to do P/Invoke and/or Marshalling. And that PostLVUserEvent function is an unmanaged API in terms of .Net. But that's not all! You need to tell the C# compiler where it can find that API by specifying the DLL it is in. However that depends on if your code is called in the LabVIEW Development system (LabVIEW EXE is exporting that function) or if you try to call your code later in a build application (lvrt.dll or possibly another DLL is exporting that function). So your C# code has to start to find out if it is called from a LabVIEW VI in the IDE or from a build LabVIEW executable!!! And that is still not all. Basically it is not feasible to try to go this route if your external wrapper DLL is written in C#, just as simple as that.

Since your actual API is also not a .Net API you are starting to build a tower by mounting a stick on a beam that is balancing on a pencil and everything is kept up by strings going everywhere.  Take a step back for a moment.

Basically your API is a standard DLL which is unmanaged, and you could write a C(++) DLL that directly interfaces this and then use PostLVUserEvent(). Or if that proofs to intimidating to you you definitely should forget about PostLVUserEvent(). Instead write a real .Net assembly that exports a proper class which has a .Net event. Then interface to this .Net assembly using the LabVIEW .Net nodes. Now you can connect to this .Net event with LabVIEW functions by registering a callback VI that is called whenever that .Net event is triggered in your assembly. That's the only feasible approach if you want to stay with a .Net assembly.

Edited by Rolf Kalbermatter
Link to comment
1 hour ago, alvise said:

I also want to pass, but I don't know exactly how. To use the PostLVUserEvent function, the extcode.h header file must be added to the C# code, but that is a separate event for how to do it in C#.

Now, after you've succeeded with C# examples, I'd advice to put them aside and try to build a very basic C/C++ DLL in your editor. Maybe start with something trivial like a sum of two numbers or even empty function, returning a constant. After you get your DLL, try to call it in LabVIEW. On success, you may go further, introducing "extcode.h", PostLVUserEvent and slowly turning your function into the callback.

Also I'd like to ask about your LV code. Why do you use Event structure without While loop? That sample should also do the job. And maybe a good idea to take NET_DVR_Logout_V30 and NET_DVR_Cleanup out of the loop, so they'd get executed anyway.

Link to comment

Thanks for your answers.

On Shaunr's suggestion, I focused on C#. I think I misunderstood the issue. But it's not bad, I gained a little more experience.

I leave aside the issue of communicating with labview using C#.
The piece of code I have created with labview so far has been created using only the HCNETSDK.dll file and this dll file has been created using C++. There is also the HCNETSDK.h file.

At the point of What can I do now, I've come to this wing, correct me if I'm wrong.
- I need to write a piece of code in C++ and in this code I need to communicate with the labview by importing the extcode.h header file and using the PostLVUserEvent function. I will do this just to understand the communication between the dll I created and the labview.
Doesn't this example here already do that?

-In C++ code, I will import the header file HCNETSDK.h and extcode.h, then call the PostLVUserEvent function and adapt the callback from labview with this function to the following snippet and finally I will compile this code and create a dll file.

 //Set exception callback function
 NET_DVR_SetExceptionCallBack_V30(0, NULL,g_ExceptionCallBack, NULL);
 //---------------------------------------
 //Start preview and set to callback stream data
 LONG lRealPlayHandle;
 HWND hWnd = GetConsoleWindow(); //Get window handle
 NET_DVR_CLIENTINFO ClientInfo = {0};
 ClientInfo.hPlayWnd = hWnd; 
 //If need to decode, please set it valid. If want to get stream data only, it can be set to NULL
 ClientInfo.lChannel = 1; // Preview channel NO.
 ClientInfo.lLinkMode = 0; /* The high bit (31) 0 means the main stream, while 1 means the sub 
stream. Bit 0~bit 30 are used for link mode: 0- TCP mode, 1- UDP mode, 2- Multi-play mode, 3- RTP mode, 4- RTP 
over RTSP, 5- RTSP over HTTP */

Below is the C++ sample code I can use.

 

I have taken into account your suggestion to run the Labview code in the while loop.

C++ live stream.cpp

Edited by alvise
Link to comment

This is a somewhat rough attempt to try to resemble your Preview sample code in LabVIEW. No callback is used, instead I use a dummy VI that is inserted in a sub panel and then use its window handle to pass to the RealPlay function. This "might" work but without anything to test with it is a shot in the dark.

HKNetSDK LabVIEW.zip

Link to comment

I was wondering if we could have a generic solution for callbacks using Variadic functions/templates or maybe the Pack Expansion.

We could prime the callback with a call to set the callback parameter list (cluster?) and target (the dll) we need and then apply the callback which has a PostLVUserEvent in its function body.

Edited by ShaunR
Link to comment
On 5/14/2022 at 10:48 AM, ShaunR said:

I was wondering if we could have a generic solution for callbacks using Variadic functions/templates or maybe the Pack Expansion.

We could prime the callback with a call to set the callback parameter list (cluster?) and target (the dll) we need and then apply the callback which has a PostLVUserEvent in its function body.

Only one “little”  problem: You need to build the datatype to pass to the PostLVUserEvent() function absolut bit exactly according to the datatype used for the event and after the call cleanup any allocated memory. And define the stack frame your callback function pointer should have and translate between the two.

A variadic function could help with some aspects of this but only in a very limited way. I haven't studied the pack expansion but don't see how that would be significantly better for this particular situation. These all do their magic mostly at compile time, but we need here something that can adapt at runtime to user specified parameters or you're back at square one as they have to compile their own C code again and using features like template classes that most C programmers makes their toes curl. 😆

You have the problem that your callback function pointer you need to provide must be able to dynamically adapt to both the calling interface (your library expecting a callback pointer has a very specific idea what parameters it wants to pass on the stack and you can't change that as it pleases you) and on the other hand it needs to be able to create a proper memory buffer to pass to PostLVUserEvent().

Variadic functions are helpful if you can define the parameter interface for it but absolutely terrible to try to make adapt to a given interface even if it does not change, which we can't expect here at all. It sort of can be hacked on some compilers to work since the va_arg is really just a memory pointer that you can actually walk but even then it is a nightmare as you have to deal with memory alignment rules that are often obscure at least. Other platforms completely hide the implementation details of va_arg and trying to hack into that is completely impossible. The better approach here would be to use libffi instead but that is going to be a very painful exercise to implement too. 😀

I don't believe that libffi already existed when NI developed the Call Library Node. If it had they certainly would have used it. It is THE library to use when a programming language tries to call other components from other languages. It is what makes Python ctypes work, and also what lets Lua access external shared libraries dynamically. Trying to do anything like this nowadays without using libffi is not just trying to reinvent the wheel, but also fire and water. 😃

I have used libffi in the past for other things. It works and can do almost anything about calling dynamic functions and providing dynamic stubs that can be used as callback pointers but that is just half of the problem you have here! And already very painful. Your computer crashes zillion times during development of this part and sometimes really hard where not just your test process crashes but your whole OS. 

And then you pass in the typedescriptor of the event data type and parse it and build the memory structure, pass in another descriptor of how to map the callback stackframe parameters to this memory structure, build the callback stub pointer according to this and then refer its invocation to your translation function that does the actual memory translations and then calls PostLVUserEvent() and afterwards deallocate everything that you just allocated. Bonus points for smart memory caching of that memory that won’t ever leak memory.

And yes if your callback function doesn't allow for a pUser data pointer of some sorts that you can use to pass all the management information your callback needs to walk its stack, build the PostLVUserEvent() data and translate the stack parameters to it you are pretty much hosed, or have to devise a very complicated scheme of a global registry that your callback function can use to refer to the according translation information. I'm not even considering the option to use a global variable in such a shared library for this as it would limit the library to only ever implement one callback in the entire program.

You could of course also resolve to build the whole callback function from scratch through creating assembly code on the fly and then do a little more gymnastics to mark that memory pointer as executable so your CPU won't bomb on you when you pass that pointer to the function as callback pointer. 😁

That's in a nutshell what it would take for such a solution. Each part of it is a challenge in itself. Making it all work together won't be trivial either. How many man month did you plan to spend on this? 😆

Why NI hasn't provided that already? Aside from the very significant work that this takes, such a configuration is a few magnitudes more complex to get just right, and considering how most LabVIEW users totally and completely struggle with simple function interface configuration already, you can for sure see where that would lead. It would have been a very unsupportable burden even back in the days when NI technical support still was top of the notch in the industry. Any manager who had been offered such a proposal would probably shoot the proposer. 💣

Edited by Rolf Kalbermatter
Link to comment
35 minutes ago, Rolf Kalbermatter said:

Only one “little”  problem: You need to build the datatype to pass to the PostLVUserEvent() function absolut bit exactly according to the datatype used for the event and after the call cleanup any allocated memory. And define the stack frame your callback function pointer should have and translate between the two.

A variadic functions could help with some aspects of this but only in a very limited way. I haven't studied the pack expansion but don't see how that would be significantly better for this particular situation. These all do their magic mostly at compile time, but we need here something that can adapt at runtime to user specified parameters or your a back at square one as they have to compile their own C code again and using features like template classes that most C programmers makes their toes curl. 😆

You have the problem that your callback function pointer you need to provide must be able to dynamically adapt to both the calling interface (your library expecting a callback pointer has a very specific idea what parameters it wants to pass on the stack and you can't change that as it pleases you) and on the other hand it needs to be able to create a proper memory buffer to pass to PostLVUserEvent().

Variadic functions are helpful if you can define the parameter interface for it but absolutely terrible to try to make adapt to a given interface even if it does not change, which we can't expect here at all. It sort of can be hacked on some compilers to work since the va_arg is really just a memory pointer that you can actually walk but even then it is a nightmare as you have to deal with memory alignment rules that are often obscure at least. Other platforms completely hide the implementation details of va_arg and trying to hack into that is completely impossible. The better approach here would be to use libffi instead but that is going to be a very painful exercise to implement too. 😀

I don't believe that libffi already existed when NI developed the Call Library Node. If it had they certainly would have used it. It is THE library to use when a programming language tries to call other components from other languages. It is what makes Python ctypes work, and also what lets Lua access external shared libraries dynamically. Trying to do anything like this nowadays without using libffi is not just trying to reinvent the wheel, but also fire and water. 😃

I have used libffi in the past for other things. It works and can do almost anything about calling dynamic functions and providing dynamic stubs that can be used as callback pointers but that is just half of the problem you have here! And already very painful. Your computer crashes zillion times during developing of this part and sometimes really hard where not just your test process crashes but your whole OS. 

And then you pass in the typedescriptor of the event data type and parse it and build the memory structure, pass in another descriptor of how to map the callback stackframe parameters to this memory structure, build the callback stub pointer according to this and then refer its invocation to your translation function that does the actual memory translations and then calls PostLVUserEvent() and afterwards deallocate everything that you just allocated. Bonus points for smart memory caching of that memory that won’t ever leak memory.

And yes if your callback function doesn't allow for a pUser data pointer of some sorts that you can use to pass all the management information your callback needs to walk its stack, build the PostLVUserEvent() data and translate the stack parameters to it you are pretty much hosed, or have to devise a very complicated scheme of a global registry that your callback function can use to refer to the according translation information. I'm not even considering the option to use a global variable in such a shared library for this as it would limit the library to only ever implement one callback in the entire program.

You could of course also resolve to build the whole callback function from scratch through creating assembly code on the fly and then do a little more gymnastics to mark that memory pointer as executable so your CPU won't bomb on you when you pass that pointer to the function as callback pointer. 😁

That's in a nutshell what it would take for such a solution. Each part of it is a challenge in itself. Making it all work together won't be trivial either. How many man month did you plan to spend on this? 😆

Why NI hasn't provided that already? Aside from the very significant work that takes, such a configuration is a few magnitudes more complex to get just right and considering how most LabVIEW users totally and completely struggle with simple function interface configuration already, you can for sure see where that would lead. It would have been a very unsupportable burdon even back in the days when NI technical support still was top of the notch in the industry. Any manager who had been offered such a proposal would probably shoot the proposer. 💣

so your objection isn't the callback, per se. It's the PostLVUserEvent data type which is a void*.

What if we fixed the data type to be an array of U8? That is the most common usage for getting data from callbacks and most types can be massaged into an array of bytes.

35 minutes ago, Rolf Kalbermatter said:

How many man month did you plan to spend on this?

I was thinking more of a Sunday afternoon while drinking a Negroni :D

 

 

Edited by ShaunR
Link to comment

Thanks for the example.

I tested this application, but after running it and pressing the connect button, the labview crashes as it is. It does not read the device's information.I need to close Labview completely.I couldn't figure out why I couldn't connect. Everything seems right though :)

Edited by alvise
Link to comment
14 hours ago, alvise said:

- I need to write a piece of code in C++ and in this code I need to communicate with the labview by importing the extcode.h header file and using the PostLVUserEvent function. I will do this just to understand the communication between the dll I created and the labview.
Doesn't this example here already do that?

You are right. By the way that is the example, that I used a while ago to study PostLVUserEvent just like you do. But you don't need the string manipulations from there. You're going to pass pBuffer pointer to LabVIEW with PostLVUserEvent function inside your callback and you should be done.

14 hours ago, alvise said:

-In C++ code, I will import the header file HCNETSDK.h and extcode.h, then call the PostLVUserEvent function and adapt the callback from labview with this function to the following snippet and finally I will compile this code and create a dll file.

Looking at your C code I see, you're doing more or less good. But you don't even need to implement the main function, because all the work with the cameras is made in your LabVIEW application entirely. You could remove that code at all. Besides the callback function you'll need one extra helper function, that would set your User Event refnum to some global variable in your DLL. That's needed, because when you'll want to call PostLVUserEvent, you'll need that refnum and you could take it out of that global variable.

Something like this:

#include <stdio.h>
#include <iostream>
#include <time.h>
#include "Windows.h"
#include "extcode.h"

using namespace std;

LVUserEventRef *pUE;

void SendEvent(LVUserEventRef *rwer)
{
  pUE = rwer;
}

void CALLBACK g_DataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser)
{
  //your callback code here
  // ...
  //PostLVUserEvent(*pUE, (void *)&pBuffer); is here as well
}

Likely will require some small fine-tuning like adding extern "C" { ... } to escape functions name mangling.

Edited by dadreamer
  • Thanks 1
Link to comment
2 hours ago, ShaunR said:

so your objection isn't the callback, per se. It's the PostLVUserEvent data type which is a void*.

What if we fixed the data type to be an array of U8? That is the most common usage for getting data from callbacks and most types can be massaged into an array of bytes.

No no. That is only one part of the problem and not the biggest one. The biggest problem is providing a callback function pointer that is matching exactly what the library expects. If you compile a C source code that implements that function then you can let the C compiler do all that work, but I was under the impression that you were looking for a solution that avoids the need to have the end user of the solution to compile a C code file that has to be also specifically adapted to the callback interface that library expects.

You need to have a memory pointer that can be used as function pointer and that provides a stack frame that is exactly compatible to the expected interface from the library you want to pass this pointer to. On the other side you need an interface that can adapt to the data type that your PostLVUserEvent() needs. By fixing this PostLVUserEvent() interface to one or two datatypes, you solve one of the problems but you still need to provide an adaptable function pointer that can match the function interface of the library for that callback pointer. And that is were C itself simply won't help you. Why should it try to do that? The C compiler is much better at figuring this out so why spend time to develop a standard that lets you do C compiling at runtime? Yes such libraries exist but that is not something a C compiler would ever consider supporting itself. Therefore variadic and pack extension were never developed for such use cases. and simply couldn't help with that.

If you don't want to have a C compiler involved in the end user solution, you need a way to express your callback function interface in some strict syntax and a translation layer that can comprehend this interface description and then prepare a call stack frame and function prolog and epilog to refer to that stack frame and at the end clear up the stack frame properly too. That's the really hard part of your proposed solution. And the user of such a solution also needs to understand the description syntax to have your library build the correct stack frame and function epilog and prolog for that function pointer. Then you could have it call into a common function that receives the stack frame pointer and stack parameter description and translate that into whatever you need. This translation from anything to something else that LVPostUserEvent() needs, can be as elaborate as you want. It's not really very difficult to do (if you really know C programming and twiddling with bits and bytes efficiently of course), just a lot of work as you need to be prepared to handle a lot of data types on either side of that translation. You can reduce that complexity by specifying that the LVPostUserEvent() interface always has to be one and only one specific datatype but it is still a lot of work.

This second part of translating a known stack frame of datatypes is in complexity similar to those LabVEW libraries out there that parse a variant and then try to convert it back into LabVIEW native data themselves. It's complicated, a lot of work and I seldom saw a library that did a really decent job at that beyond serving some basic datatypes but it can be done with enough determination.

The REAL problem is however to let the user build a callback pointer that matches the expected calling interface perfectly without having him to execute a C compiler. This is only really possible with a library like libffi or similar, if you do not intend to go and start playing assembler yourself. There is no compiler support for this (unless maybe if you would like to repurpose llvm into your own library) since it makes little sense to let the compiler externalize its entire logic into a user library. The gcc developers have no interest to let a user create another gcc like thing, not because they shy the competition but because the effort would be enormous and they have enough work to churn on to make the compiler work well and adapt to ever increasing standard proposals.

libffi allows to build callback pointers, but it needs of course a stack frame description and other attributes such as calling convention to be able to do so. And it needs a user function pointer to which it can forward control after it has unraveled the stack and which after this function has done its work it will execute its epilog to do any stack cleanup that may be required. If LabVIEW had an officially documented way to call VIs from exernal code, I would have long ago tried to do something like that, as it would be very handy to have in Lua for LabVIEW. Currently Lua for LabVIEW does all kinds of very involved gymnastics that also involve a background LabVIEW VI deamon to which the shared library passes any requests from the Lua code to execute VIs (well the VIs are really registered in Lua as simply other Lua functions so the Lua script has no idea that it is effectively calling a LabVIEW VI), which in turn then calls that VI from its LabVIEW context and passes any return values back. Since this is such a roundabout way of doing things it has to limit the possibilities. A LabVIEW VI can execute Lua code which calls back into LabVIEW VIs but after that it gets to complicated to manage the necessary stack frames across calling environments and the Lua for LabVIEW shared library has specific measures to detect such calls and simply forbids them categorically for any further round turn.

It also has a very extensive function to translate Lua data to LabVIEW data based on the LabVIEW typedescriptor and an according reverse function too that handles almost all the data types that LabVIEW and Lua know. But I can't directly invoke a VI from within the external code since there is no documented function to do so. Yes I know that there are functions that can do it but without a full documentation about them I will not embark on using them in any project that will leave my little office ever.

Basically your desired solution has two distinct areas that need a solution:

1) Create a function pointer with correct prolog and epilog according to some user specified interface description that will then call the function in 2)

2) Create a function that receives the stack frame information and datatype description and then uses another user specification that defines which of those stack frame parameters should be used and how and in what kind of datatype they should be translated to pass to PostLVUserEvent()

 

1) can be solved by using libffi, but it is not going to be easy. libffi is anything but trivial to use but then it does solve a problem that is also anything but common in programming.

2) is simply a more or less complex function that can be developed and tested independently from the rest. It is NOT the big problem here, just quite a bit of work.

If the callback allows to pass a user data pointer, you can repurpose that to pass all the information from about how the stack frame is build and how to translate from the stack frame to the PostLVUserEvent() from the setup function that prepares the callback pointer through this pointer. If it has not such a user data pointer , you have an additional problem of how to provide the necessary information to your translation function. It may be possible to prepare the callback pointer with some extra memory area to hold that pointer such as prepend it directly in front of the actual entry point and dereference it with a negative offset inside the callback but that is going to be highly hacky and has quite a big chance off breaking on some platforms, CPUs or OSes.

Edited by Rolf Kalbermatter
  • Thanks 1
Link to comment
1 hour ago, ShaunR said:

Your still missing an important point here. The problem solved in that link is about calling an arbitrary function interface. That is "easily" solved with libffi, and the other solution mentioned in there is with the boost library, which does it with C++ templates and overrides. This is the same what the Call Library Node already provides in LabVIEW too and there are many other solutions out there. C# has P/Invoke for that for instance. If given the choice I would go with libffi before boost at anytime. It's a much more limited and self contained dependency than the whole boost library and also supports many more architectures, OSes and compilers than the boost library ever could.

But what you want is the opposite, you want a function implementation that needs to provide an arbitrary interface that is not defined at compile time. That is basically turning the glove inside out, so to speak. Now I can't vouch about boost having that capability, it may have but I don't know. I know that it can be done with libffi since Python ctypes supports callback function declarations that call back into Python functions. P/Invoke also supports callback function declarations that then call C# functions, but there are very few examples out there how to do it (as are for Python ctypes too by the way).

And I have not yet used libffi myself to implement callback functions but it is a fairly different class of problem than building a dynamic definition to call an arbitrary function, which libffi was mostly built for in the first place. The capability to also support building function pointer definitions that can be called from other code only came later to it, as you can also see from the comment from Basile Starynkevitch in your linked thread, who still claims that it can not be done with libffi. It can nowadays but the way it does it is by using quite some platform specific assembly code and I would hesitate to expect the more exotic platforms that libffi supports to have fully working code for that.

There is also the very likely possibility that doing that will possibly fail on future CPU platforms outside of highly privileged contexts, such as inside the kernel, as they tend to crack down more and more on dynamic modification of executable memory segments or turning normal heap memory into executable memory segments. It's basically self modificating code and that can be used for very evil purposes. Any modern OS is almost obligated to prevent such things at all costs if it wants to have the necessary security certificates to be allowed to be used in various government and high security environments. libffi and other such libraries still can work for supporting callback function pointers, but that may simply end sometimes in the future. 

 

Edited by Rolf Kalbermatter
Link to comment
4 hours ago, alvise said:

Thanks for the example.

I tested this application, but after running it and pressing the connect button, the labview crashes as it is. It does not read the device's information.I need to close Labview completely.I couldn't figure out why I couldn't connect. Everything seems right though :)

The connect function failing is something that is a bit unexpected to be honest, I was more expecting problems with Start. But there could be many reasons. For instance it seems that this SDK requires the two HCCore and HCNetSDK shared libraries. I tried to make sure they are both loaded, but that may have been wrong and make things worse. Where did you have them in your LabVIEW example?

It seems logical that the HCNetSDK also requires many of the other DLLs that the SDK comes with and it is totally undocumented how it will try to find them if at all. It may simply try to load them and not even check if that loading was successful and then trying to call functions in them, which of course would fail catastrophically. Without having a camera to test in real this is basically as much as I can do and then you are on your own.

Edited by Rolf Kalbermatter
Link to comment

 

image.png.410f8cb5fdfa3ce8325f93f4e103e0d4.png   Do I need to add something like the following to the empty.vi file here?

Quote

image.png.49e23f0126d2b0ba76d1802e75c04934.png

                                       The .dll file created with the Call library function here should be called.

and the dll file to be created should contain the following code.

Is there such a situation?

Link to comment
42 minutes ago, alvise said:

 

image.png.410f8cb5fdfa3ce8325f93f4e103e0d4.png   Do I need to add something like the following to the empty.vi file here?

                                       The .dll file created with the Call library function here should be called.

and the dll file to be created should contain the following code.

Is there such a situation?

Nope. That is something entirely different! I'm not making use of any message hooking library here that you might need to prime a some point. 

I'm simply using that empty VI to get a front panel whose window handle I can use to pass to the RealPlay function so it has a graphics port to draw its image into. That's it. I could have used the main VI instead but that would then cause the RealPlay function to attempt to draw into that and by doing so fight with LabVIEW trying to update the button areas as you press them. With this empty VI there is no LabVIEW trying to fight with the RealPlay task and you can insert it into a subpanel anywhere on a front panel and have other things besides it on that front panel

However the logic is such that you have first to connect and only in start is the windows handle then passed to the SDK function, so your crash during the connect function has nothing to do with it since that VI is not yet active at that point. 

Link to comment

When the new calback function worked the correct HCNetSDK location, it created a connection and read the camera's information. Now the connect and Start button works, the labview does not crash, there is only a small problem, there is no image.
-What could be the reason for this?

image.png.0fe55017c290f267905aacb8b80e408d.png

The description here mentions the callback event. That shouldn't be a problem, right?

Did you choose NET_DVR_RealPlay specifically because it has this function NET_DVR_RealPlay_V30

Edited by alvise
developing
Link to comment
1 hour ago, alvise said:

When the new calback function worked the correct HCNetSDK location, it created a connection and read the camera's information. Now the connect and Start button works, the labview does not crash, there is only a small problem, there is no image.
-What could be the reason for this?

image.png.0fe55017c290f267905aacb8b80e408d.png

The description here mentions the callback event. That shouldn't be a problem, right?

Did you choose NET_DVR_RealPlay specifically because it has this function NET_DVR_RealPlay_V30

My library does not use ANY callbacks. That was not the aim of it! Instead I tried to implement the Preview example code that you showed in a LabVIEW way. There is a callback in your Preview example code but it is NOT meant for receiving image data but to send exceptions to an app. Exceptions are when the library sees abnormal things such as errors happening and then it will send an according exception message through this callback. I completely omitted this callback as it does not seem that important to me. Instead I implemented proper error handling for all SDK functions that get called. That should be more than enough for now and that exception callback could be added once everything else is working perfectly.

This SDK seems to have a number of different options. You can simply connect to the camera and retrieve an image occasionally and save it to disk. You can pass a windows handle in the lpClientInfo struct and the driver then is supposed to draw its image data into that window. This is what I tried to implement. And you can install a number of callback functions which are then continuously called by the driver to pass the image data frames to it. This callback function is the hard part and not just because you have to provide it. This will require you to write some C code to implement that function and then you have to find a way to pass this data back to LabVIEW, which the PostLVUserEvent() function MIGHT help with. But the data that this function receives is in a, let's say barely documented format. They talk a little about a proprietary format, and mention repeatedly that it can also contain simply network packets, which would be the H264 encoded data stream and you are back at square one.

Also the SDK documentation is a little hard to read. Sometimes the text is hilarious, but sometimes it is simply confusing

Extra note: are you by any chance using 64-bit LabVIEW? If so check your HKNetSDK:Start function. In there is a conditional compile structure and the 64-bit case is missing a very important wire.

HKNetSDK_Start.png

Edited by Rolf Kalbermatter
Link to comment

Do I understand correctly what you are saying? I still need to create a .dll in C++ to use the labview code you shared here.

I need to compile the code below (after adapting the part added as C# code to C/C++ language) and create the .dll file, right?

Quote

 

#include <stdio.h>
#include <iostream>
#include <time.h>
#include "Windows.h"
#include "extcode.h"

using namespace std;

LVUserEventRef *pUE;

void SendEvent(LVUserEventRef *rwer)
{
  pUE = rwer;
}

void CALLBACK g_DataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser)
{
 

                byte[] sData = new byte[dwBufSize];
                Marshal.Copy(pBuffer, sData, 0, (Int32)dwBufSize);

                string str = "test.ps";
                FileStream fs = new FileStream(str, FileMode.Create);
    Here is the example created in C#. But I have to convert it to C/C++ code.
                int iLen = (int)dwBufSize;
                fs.Write(sData, 0, iLen);
                fs.Close();  

  PostLVUserEvent(*pUE, (void *)&pBuffer); is here as well
}

 

If this step is correct I think I need to make some plugin in the labview code you shared that should get this callback.Live video output can be seen after adding the callback plugin as well.

 

There is probably nothing as difficult as putting the pieces together in this confusion.

-Not:I don't have a chance to use 64-bit labVİEW, currently I can only use 32-bit.

 

Edited by alvise
Link to comment
14 hours ago, alvise said:

Do I understand correctly what you are saying? I still need to create a .dll in C++ to use the labview code you shared here.

I can't speak for anyone else, but from what I saw, Rolf's example should work "as is", without any callbacks or something extra. I can't say, why it doesn't work on your side. But it should.

14 hours ago, alvise said:

I need to compile the code below (after adapting the part added as C# code to C/C++ language) and create the .dll file, right?

Well, you have two options here. You may try to figure out, why Rolf's example doesn't want to work, and fix it on your own. Or you could write the callback DLL and integrate it into your previous samples (from page one of this thread). Of course, if you can fix Rolf's example and write the callback, then you may combine two options into one and get best example for that camera.

14 hours ago, alvise said:

If this step is correct I think I need to make some plugin in the labview code you shared that should get this callback.Live video output can be seen after adding the callback plugin as well.

Don't insert any C# code, just try to compile it into a library and report the progress. You should receive a standard DLL, that's exporting two functions: SendEvent and g_DataCallBack. Check that they both are present.

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
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
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.