Jump to content

Rolf Kalbermatter

Members
  • Posts

    3,786
  • Joined

  • Last visited

  • Days Won

    245

Posts posted by Rolf Kalbermatter

  1. The exact value of the session variable is not to be interpreted in any way other than what the API documentation states. And in there it says:

    Quote

    -1 means false, and other values could be used as the handle of functions like NET_DVR_StopRealPlay. Please call NET_DVR_GetLastError to get the error code.

    It's a bit unclear thanks to the non-native people who wrote that SDK. But I interprete that as follows.

    Quote

    -1 means error, please call NET_DVR_GetLastError to get the error code.

    any other values are a valid session handle that can be use with functions like NET_DVR_StopRealPlay.

    This specifically means that a value of 0 should be also valid.

    Typically these "handles" are simply an index into an API private array of structures that contain the actual data to manage the session/connection/resource.

    Quote

    I tried and tested a change as you said in the attached VI.

    Just changing the definition of the Call Library Node is NOT sufficient. You also need to change the C code to actually return a specific value!!!!! When you define the Call Library node for SetCbState() to return a value, the implementation of that funciton in your C code must explicitly return a value or the Call Library Node will simply return a random value (more specifically: whatever the function left dangling in the eax register of the CPU).

  2. 1 hour ago, alvise said:

    -The "SetCbState" value gets a new random value when the VI is completely shut down and restarted. For example, if VI is opened, the values will show as ''0'' and when run it will return ''324433408''.
    If the VI is completely closed and reopened, it gets the value "418927872". However, in the numbers I gave above, the last value is one up and one down (418927871-418927872) in sync with the "Stream" button.

    Did you change the function to actually return a value? In the last code you posted the SetCbState() function returns void. That means you can configure the Call Library Node to return an integer value, but what it will return is random data, or more precisely whatever the function was last using the eax register for. Since it is declared to have a void return value, the compiler does not make sure to return anything meaningful in there.

    I would change the actual InstallCallback function as follows:

    //If the above if condition does not occur, the LVUserEventRef here does not take a value.
    typedef BOOL(__stdcall* Type_SetStandardDataCallBack)(LONG lRealHandle, void(CALLBACK* fStdDataCallBack) (LONG lRealHandle, DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize, DWORD dwUser), DWORD dwUser);
    extern "C" __declspec(dllexport) MgErr __cdecl InstallStandardCallback(LONG lRealHandle, LVUserEventRef * refnum)
    {
        HMODULE hDLL = LoadLibraryW(L"HCNetSDK.dll");
        if (hDLL)
        {
            Type_SetStandardDataCallBack installFunc = (Type_SetStandardDataCallBack)GetProcAddress(hDLL, "NET_DVR_SetStandardDataCallBack");
            if (installFunc)
            {
                BOOL retval;
                if (refnum && *refnum)
                    retval = installFunc(lRealHandle, DataCallBack, (DWORD)(*refnum));
                else
                    retval = installFunc(lRealHandle, NULL, 0);
                if (retval)
                    return mgNoErr;
                else
                    return 5000 + NET_DVR_GetLastError();
            }
            FreeLibrary(hDLL);
        }
        return mgNotSupported;
    }

     

  3. 5 minutes ago, alvise said:

    By the way, I'm not doing this to get a payment. I'm just doing it to help someone. When I started to create this Application, I honestly did not anticipate that it would be such a deep job. Therefore, I never anticipated the time it would take.

    I had some suspicion in this direction. 😃 Anyone doing this for money would either have given up long ago because the available budget was running out very quickly or having been shot by the client for not delivering on his promises. 😆

    But it doesn't mean that it makes the work more feasible.

  4. 4 hours ago, ShaunR said:

    I think that's a bit harsh.

    I'm sorry if I sound harsh. We have a Dutch saying here. Translated it says about this:

    Quote

    gentle healers make stinking wounds

    I felt it is time to be a little harsh here. alvise is strampling and barely keeping his head above the water and he has not even started to realize that his ordeal is far from over. Making the callback function work is only a first step on a very long path to get the actual video data into an image format that he can use in LabVIEW!

  5. 2 hours ago, alvise said:

    Yes, I know it's something not everyone can do, but there is no alternative. By the way, I don't understand what you mean by "declined to quote" :)

    It's something not just not everyone can do, but very few who work with LabVIEW. Usually people choose to work with LabVIEW because they are not programmers but want to solve something. Writing DLLs is a lot more complicated, interfacing them to LabVIEW somewhat more. Doing Video handling even just in LabVIEW alone is pretty hard, and trying to combine all these things together is VERY hard.

    As to the quote statement, there are two ways to start a project when someone asks you to do it. You make an estimate of the required time, slap some security margin on top of that and multiply it with your hourly rate and quote that to the client. Or you start programming and charge him by the hour as you go. This last version more often than not ends in the situation where your customer wants to see results before paying even one dollar more and you are still far from having anything to show him. So it is a very bad model to work with, but if the problem is hard and your experience with it limited you are in no position to make a good estimate, so all you can do in good faith is to refuse the invitation to submit a tender bid.

    Even I would have to think twice about taking on such a contract and I'm absolutely not kidding here. As explained before, once the callback is working you still have NOTHING. The data is compressed and decompressing video data in an efficient manner is never easy even if you can use ready made libraries that handle your specific compression format perfectly. As it is we don't even know exactly how that video stream is compressed nor how the individual bits are packaged into it.

  6. 1 hour ago, alvise said:

    No, no changes have been made here.

    Communication with you is sometimes rather difficult. The code he shows is different to the original code I posted many many moons ago, long before the post with the new code that I referenced now (and which he shows). So he asked if you had applied that change too, as otherwise calling the InstallStandardCallback function with a NotANumber refnum would have zero effect.

    And you answer "No, no changes have been made here". It shows me two things:

    - You don't remember what you did a few days ago

    - You really don't understand what you are doing here

    I wonder if you know how to do C source code level debugging? You can have the Visual Studio Debugger show where in the code you are, when building the DLL in Debug mode (which as you had to find out earlier can have certain implications while compiling but it can help to verify what you are doing on the C code level). It's tricky to get Visual Studio do the right thing and it's even trickier to not get yourself tied up with legs behind your neck and arms under you ass when trying to do that with callback code, but it can be done. But I hesitate to mention this option as I'm not sure you will be able to get yourself untied after venturing down this path. 😆

    More likely than not you will get yourself many times in a deadlock situation where you should acknowledge something in LabVIEW to be able to proceed but can't because the execution flow is stopped in Visual Studio and LabVIEW is blocked by that, but when you continue code execution in Visual Studio it goes way to fast to let you do that acknowledgment in LabVIEW. Only solution at that point: Ctrl+Alt+Del, look for your LabVIEW and Visual Studio process in the task list and kill them. Hopefully you saved all your work before starting your test, because otherwise it's gone. It's tedious and frustrating to have to work like that, but for me I could never get DLL code properly working without at least some source level code debugging in C. That is because I do not just want a DLL not to crash. Once its execution doesn't crash you are not even half done. You need to make sure that it does not sneakily overwrite buffers anyways somewhere. If you don't or can't do that debugging, you are only creating a huge liability for yourself and whoever you give this code later on to use it. Such buffer overwrites may not crash your system now, but they do destroy data in memory. If this is measurement data for your multi-million $ experiment, some driver structures that under extreme situations might cause actual physical or integral damage to your hardware, or just by sheer luck only destroy unused memory somewhere, you don't know. But you can be of one thing sure, it won't always do the same thing. Depending on memory layout of your machine, which can depend on many things such as what other applications are running alongside or which OS revision you are running (yes even a minor OS update could have a huge impact on that), your "harmless" buffer overwrite suddenly turns into a 7 fanged dragon that consistently crashes your process and possibly even others in very nasty ways. Or it silently destroys your experiment measurement data and makes your whole program more than utterly useless.

    One test I do is to run such code on at least 3 different LabVIEW version and if it is by any means multiplatform, also on all the possible platforms like Windows, Linux and Mac. This catches most such bugs in one way or the other at some point. Yes it is a lot of work, and yes it is tedious but the chance to release a shared library that corrupts memory is simply to big otherwise.

  7. 18 minutes ago, alvise said:

    From what I understand so far, uninstallcalback should be created in dll and use in labview with Not A refnum (no reference) to delete callback if stop button is pressed.
    The dll file should understand this no-reference status and run the remove function and remove the callback.It should release the library for that.

    I need to create 2 parameters in this function.1-Return 2- for ''Not A Refnum'' connection.

    I'm experimenting by doing research and commenting out the existing code.
    But I haven't been able to find exactly how the code needs to be able to perform the callback function.

    I came to the following conclusion.I guess it should be created as follows, right?

    typedef BOOL(__stdcall* Type_SetStandardDataCallBack)(LONG lRealHandle, void(CALLBACK* fStdDataCallBack) (LONG lRealHandle, DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize, DWORD dwUser), DWORD dwUser);
    extern "C" __declspec(dllexport) BOOL __cdecl unInstallStandardCallback(LONG lRealHandle, LVUserEventRef * refnum)
    {
        HMODULE hDLL = LoadLibraryW(L"HCNetSDK.dll");
        if (hDLL)
        {
            Type_SetStandardDataCallBack uninstallFunc = (Type_SetStandardDataCallBack)GetProcAddress(hDLL, "NET_DVR_SetStandardDataCallBack");
            if (uninstallFunc)
            {
                FreeLibrary(hDLL);
             
            }
            
        }
    
    }

    I don't know if I have established a correct logic

    Either I'm really walking towards a light or at the end of the road I will realize that that light is the light that the firefly emits to catch its prey, of course, it is too late for everything now :)

    Go back to this post and look at the code and the explanation I gave there!

    If you want to stay with analogies, it could be also the headlights of the train that is running to roll over you. 😆

  8. 37 minutes ago, alvise said:

    At first, U32 was a numeric number, I directly converted it to an array and set the data type to U8. Later, when the problem occurred, I tried to test it with U32 numeric again.
    But when I got picture as U8 I was testing as U8
    Later when making changes I forgot to change it back to U8. :)

    This type of error is very quickly the difference between a crashing monster and a well behaving application. There is no LabVIEW who can save you from this. It is YOU who has to make sure that what you write in the external code, matches exactly what you tell LabVIEW to expect. It must be able to trust you blindly on this or your library is nothing more than a bunch of crashing dummies. If you can't make sure of that because you obviously don't understand the actual implications and/or easily forget about them after they have been explained to you a few posts earlier, then you are truly playing with fire in trying to create such a library. Seriously!

    Quote

    I think the problem still persists. Again sometimes the data is not being read.

    That are the statements I love in every problem description. Very precise and to the point, NOT!

    How does it look when you conclude it is not read? Does just the array get empty data? Do the other values such as lRealHandle turn to show a different value too? And which one?

    Right now it is like I show you a picture of a nice car (your image shows that there is actually data) and then tell you he doesn't sometimes work. Can you help me? Who could except someone with clairvoyant abilities?

    Does it start? Does it suddenly stop? Are you sure there is gas in the tank? Do any lights in the dashboard light up?  You see what I'm saying?

  9. 18 minutes ago, alvise said:

    After closing and reopening the project completely I am facing a situation as below.dwdatatype returns value as 0.

    It doesn't return "anything". That indicator only gets written to in the LV User Event case. As long as you do not see such an event in the Event Inspector Window it simply means that the LVPostUserEvent() function either was not called or not with the correct user event refnum that your user event structure is registered to.

  10. 21 minutes ago, dadreamer said:

    If I got it right, he says that on one run of the VI the events are being sent OK, but on another run (after he stops the VI and runs it again) no events is sent regardless of the button being pressed.

    Could it be that he simply did not activate the button again but left it pressed as it was? That state is only send on state change of the button, not because LabVIEW magically doesn't know that it should call that event case again to send the True state to the DLL.

  11. 1 hour ago, alvise said:

    I made the correction as you said, and I am getting the following log records. Currently, no Realhandle data is visible.

    EventLog-15.txt 35.15 kB · 1 download

    I have absolutely no idea what you mean when you say that no RealHandle data is visible. As far as that log looks, no event is ever sent. This could be happening if your cbState boolean was never set to true.

  12. 30 minutes ago, alvise said:

    Right now I made a change as you said. I get the following output.

    Is it normal to get an output like the one below? Because sometimes I get Realhandle data. Sometimes I don't.

    I have no idea what you mean. It doesn't look wrong to me. The lRealHandle is always 0, which according to the SDK documentation is a valid session handle and should match the value that you pass to InstallCallback after having started the camera stream.

    The dwDataType is 1 in the first package which indicates that it is a SysHead package with some initial information about the stream as documented in some of the SDK C samples. dwDataType = 2 seems not to be documented in the SKD help for the StandardDataCallback. But it is for the RealDataCallback which states that it is simply stream data, so not likely meant to be decoded yourself but simply streamed to a file as .ps file as in the SDK sample. And there we are back at the start. To get such a .ps file you could also simply have called NET_DVR_SaveRealData() and forget about all the callback hassles.

  13. 21 minutes ago, dadreamer said:

    This explains why he was seeing unstable behaviour from run to run. This is what happens, when two advisers are trying to help simultaneously. 😃 I often prefer a slightly different way to transfer data from the callback. I post a pointer to data and in LabVIEW I read it out with MoveBlock (because I like to see a minimalistic external code and do most of the coding in LV instead).

    By the way, I kind of think posting an U32/U64 number might work, if DSDisposeHandle would be moved to the LV Event frame from the callback code and in that frame MoveBlock + DSDisposeHandle would be called.

    Yes it would work, if you make sure to call the DSDisposeHandle function on EVERY event, without inhibiting its call through some intermediate error cluster handling AND without having other programming errors AND without trying to single step through the code AND, AND, AND.

    I prefer to make it as stable as possible rather than as unmaintainable as possible. 😆 Your approach is fine if you get it fully debugged and then slap a password on the VI to prevent the uninhibited from peaking into your VI and mess with its inner workings. Giving the average LabVIEW user even the possibility to tinker with any pointers, is a sure way to make them create a mess.

    You mean to say it is their fault if they do that? Sure it is, but many do not have the knowledge to understand that it is. 😀

    And if you want to make it 32-bit and 64-bit compatible you have to choose between two uglies that way:

    1) define the handle explicitly as U64 on the C side and typecast it to that to pass it to LabVIEW. And treat it as U64 on the LabVIEW side just as any pointer sized integer.

    2) create conditional compile code with the event cluster definition. This would be über-ugly.

  14. 27 minutes ago, alvise said:

    Why was the "Datacallback" function in the picture above created, is it necessary to get information using that function?

    First the DataCallback() wasn't "created" but "exported. It needs to be present in the DLL so you can pass its address to the according SDK function. But it does not need to be exported from the DLL for that. As long as the Calling convention is configured correctly it will work without exporting it.

    And the thing that causes the linker to add that function to the DLL export table is your

    extern "C" __declspec(dllexport)

     in front of the DataCallback declaration. It is unnecessary and only helps to confuse you.

    Simply defining is like this should be enough:

    void CALLBACK DataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE * pBuffer, DWORD dwBufSize, DWORD dwUser)
    {
         ...
    }

     

    Quote

    Now I'm trying to figure out how can I send the true-falsa flag.
    If I'm not mistaken, I had to create something like below, but now the labview is crashing.

    Your nose really is barely above the water level. Take a break, and a deep breath!

    You simply need to create a VI that calls the SetCbState() function.

    And ohhhhhhhhhhhh my God!!!!!! You defined the event data cluster totally wrong. That "handle" is not an integer value. It is a full and complete LabVIEW byte array!! It shouldn't cause your crash .. at least not immediately! But since you don't tell LabVIEW that it is a handle it can't manage its memory. Meaning with every message that is sent to your event loop, you simply leak that handle each time. That will drive your application into out of memory in no time, just as the incorrect handling of the cbState boolean did. You not only were leaking handles when the cbState boolean was false but with EVERY single callback!!!!!!!!

  15. But you do realize that once you got the callback reliably posting binary data to your event loop, all the problems up to now are pretty much peanuts in comparison? That bytestream is compressed, with a format that depends on the parameter dwDataType and some information in the data stream, likely not in every package but at specific moments that you have to detect based on the context of earlier packages (which you hopefully haven't suppressed with your inhibit boolean).

    And that compression is H264 or maybe MPEG4 or similar for the video data and G722 or such for the Audio packages, but the documentation also talks about other possible formats, most likely depending on the camera model and/or its settings. Going to decode H264 or MPEG4 in LabVIEW itself is going to be a no-no. It may technically be possible but I would not consider being able to write a video decompressor in C and even less in LabVIEW. the math and programming needed for that is simply to complicated. Which leaves only one option: finding an external DLL and interfacing to it and believe me, interfacing to a video decompressor is no easy feat. Your exercises so far would seem almost trivial in comparison, and you are already swimming with your nose barely at the surface.

  16. I would do it like this:

    First it is important to move the NumericArrayResize() call into the same condition level as the DSDisposeHandle() call. Your code currently leaks a handle every time the callback is called while the cbState boolean is on LVBooleanFalse, which is VERY bad. The fact that it does unnecessary work is not perfect bad a very minor problem in comparison.

    // cbstate does a state reassessment.True or False
    extern "C" __declspec(dllexport) void __stdcall DataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE * pBuffer, DWORD dwBufSize, DWORD dwUser)
    {
        if (cbState == LVBooleanTrue)
        {
            LVEventData eventData = { 0 };
            MgErr err = NumericArrayResize(uB, 1, (UHandle*)&(eventData.handle), dwBufSize);
            if (!err)// send callback data if there is no error and the cbstatus is true.
            {
                LVEventData eventData = { 0 };
                MgErr err = NumericArrayResize(uB, 1, (UHandle*)&(eventData.handle), dwBufSize);
                LVUserEventRef userEvent = (LVUserEventRef)dwUser;
                MoveBlock(pBuffer, (*(eventData.handle))->elm, dwBufSize);
                (*(eventData.handle))->size = (int32_t)dwBufSize;
                eventData.realHandle = lRealHandle;
                eventData.dataType = dwDataType;
                PostLVUserEvent(userEvent, &eventData);
                DSDisposeHandle(eventData.handle);
            }
        }
    }
    
    //If the above if condition does not occur, the LVUserEventRef here does not take a value.
    typedef BOOL(__stdcall* Type_SetStandardDataCallBack)(LONG lRealHandle, void(CALLBACK* fStdDataCallBack) (LONG lRealHandle, DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize, DWORD dwUser), DWORD dwUser);
    extern "C" __declspec(dllexport) BOOL __cdecl InstallStandardCallback(LONG lRealHandle, LVUserEventRef * refnum)
    {
        HMODULE hDLL = LoadLibraryW(L"HCNetSDK.dll");
        if (hDLL)
        {
            Type_SetStandardDataCallBack installFunc = (Type_SetStandardDataCallBack)GetProcAddress(hDLL, "NET_DVR_SetStandardDataCallBack");
            if (installFunc)
            {
                if (refnum && *refnum) 
                    return installFunc(lRealHandle, DataCallBack, (DWORD)(*refnum));
                else
                    return installFunc(lRealHandle, NULL, 0));
            }
            FreeLibrary(hDLL);
        }
        return FALSE;
    }

    Then write an Uninstall Callback.vi that is essentially the same as the Install Callback.vi but don't pass in any user event refnum. Simply pass a NotARefnum to the second parameter of the Call Library Node.

  17. 1 hour ago, dadreamer said:

    Why did you leave eventData declaration and NumericArrayResize call outside the condition? Unnecessary work for the callback to do, if the flag is False.

    The eventData declaration is harmless. This is a stack variable space and gets allocated anyhow on function entry by reserving the according stack space. In the worst case it adds a single MOV rrx, sp+x call before the condition calculation, but it might do that even if you put it inside the if statement because of C compiler optimization to prevent CPU pipeline stalling.

    The call to NumericArrayResize() is a different story. This can be a potentially relatively expensive call, so should indeed only be done when necessary.

  18. 23 minutes ago, dadreamer said:

    Looks to me like some headers conflict... Try to #include "pch.h" as the first header file (before any other headers). Also you may remove all that C:\Program Files\... stuff from the #include directives and leave header names only, as you already specified cintools path in the project settings. If that doesn't help, disable precompiled headers to see, what happens.

    Besides, those %28 and %29 characters on your screenshots look odd to me, because I had not noticed before that Visual Studio escapes some symbols in path names. Do they get added automatically?

    That pch.h file is for "PreCompiled Header" its an option where the compiler creates a precompiled header file for all the different headers in your project. Can be useful when you have a project that has "zillions" of source files that include "quadrillions" of header files to reduce the compilation time as the compiler doesn't have to process each header file over and over again. For a project of this size it is useless and only causes extra trouble. There should be a setting in the compiler settings called "Use Precompiled Header file" or something like that. Disable that! Then you can also remove that include.

     

     

  19. On 5/28/2022 at 1:45 PM, dadreamer said:

    Since BitBlt is used, your PictureBox object should be visible and on screen completely, because when it's not, then black areas are captured. PrintWindow on the other side should deal with (partially) hidden windows better, but I have no idea why it doesn't work in your case.

    I do. PrintWindow sends a window message to the window function to redraw itself (WM_DRAW). The PictureBox has installed that windows function when creating the window for its drawing canvas and that function dutifully does redraw its empty PictureStream into the provided graphics context. It does not know about the bitmap that the SDK driver sneakily blitted into its window behind its back! And therefore that bitmap does not show in the PrintWindow result.

  20. 9 hours ago, dadreamer said:

    I meant this part:

    image.png.0ca1697625027e333ffa6b7273ff980b.png.0ac882a5b2475fe59c68c2abe32b9ec9.png

    It just makes no sense to capture Empty.vi window and a little sense to capture the whole main VI window, when a concrete .NET window region is wanted. Ok, when drawn onto Empty.vi panel, the whole window must be captured.

    It wasn't me who brought that back into the discussion. I was under the strong impression that we were already working for some time on the PictureBox solution as drawing canvas area. alvise suddenly brought this Empty.vi subpanel solution back into the picture.

  21. 3 hours ago, Gribo said:

    You can copy the snippet. Explanation: The Bitmap is a constructor node, and you give it the image reference. Then you get the data, in this case, one RGB pixel at 0,0. If this works, the bitmap object has a method that returns the handle to the data.

    Edit: The Bitmap constructor is in System.drawing.

    If you are worried about tearing (capturing data from 2 different frames) you can use the PictureBox' VisibleChanged LoadCompleted callback.

    That callback will almost certainly never get triggered! The SDK is not aware about that it is drawing into a .Net PictureBox. It only sees the window handle that is used by the PictureBox for its drawing canvas. And that works on a level way below .Net in the Windows window manager inside the kernel. If you would try to do anything with that PictureBox such as drawing lines or anything into, it you would get very nasty flickering as the SDK function trying to bitblit into the windows device context (HDC) will fight with the PictureBox functions who tries to do GDI drawing into the same HDC. We are abusing here the PictureBox simply as a container to provide a window handle. In this way we can let Windows window clipping handle all the issues about making sure that the SDK can't draw beyond that area provided by the PictureBox control. But for the rest we are not really using any functionality from the PictureBox .Net control. Respectively when we tried to retrieve the image, that failed since the PictureBox control is not aware about what was drawn into its window. And the same applies for the LabVIEW Get Image control function.

×
×
  • Create New...

Important Information

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