Rolf Kalbermatter Posted June 2, 2022 Report Share Posted June 2, 2022 (edited) 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. Edited June 2, 2022 by Rolf Kalbermatter Quote Link to comment
alvise Posted June 2, 2022 Author Report Share Posted June 2, 2022 actually it has a very simple answer. I don't know exactly how to do it. I guess sometimes "NET_DVR_SetStandarData CallBack" causes unreadable data. #include "extcode.h" #include "hosttype.h" #include "HCNetSDK.h" #define LibAPI(retval) __declspec(dllexport) EXTERNC retval __cdecl #define Callback(retval) __declspec(dllexport) EXTERNC retval __stdcall // Define LabVIEW specific datatypes to pass data as event // This assumes that the LabVIEW event datatype is a cluster containing following elements in exactly that order!!! // cluster // int32 contains the current live view handle // uInt32 contains the dwDataType (NET_DVR_SYSHEAD, NET_DVR_STD_VIDEODATA, NET_DVR_STD_AUDIODATA, NET_DVR_PRIVATE_DATA, // or others as documented in the NET_DVR_SetStandardDataCallBack() function // array of uInt8 contains the actual byte stream data #include "lv_prolog.h" typedef struct { int32_t size; uint8_t elm[1]; } LVByteArrayRec, * LVByteArrayPtr, ** LVByteArrayHdl;// ** LVByteArrayHdl;pointer to a pointer typedef struct { LONG realHandle; DWORD dataType; LVByteArrayHdl handle; } LVEventData; #include "lv_epilog.h" extern "C" __declspec(dllexport) void __cdecl SetCbState(LVBoolean * state); // where Standard Boolean type case is set: typedef uInt8 LVBoolean LVBoolean cbState = LVBooleanFalse; // always set to false on startup extern "C" __declspec(dllexport) void __cdecl SetCbState(LVBoolean * state) { cbState = *state; } // cbstate does a state reassessment.True or False // 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; } This is how it looked when you first posted the code (referring to the code dadreamer shared). typedef BOOL(__stdcall *Type_SetStandardDataCallBack)(LONG lRealHandle, fStdDataCallBack cbStdDataCallBack, DWORD dwUser); LibAPI(BOOL) InstallStandardCallback(LONG lRealHandle, LVUserEventRef *refnum) { HANDLE hDLL = LoadLibraryW(L"HCNetSDK.dll"); if (hDLL) { Type_SetStandardDataCallBack installFunc = (Type_SetStandardDataCallBack)GetProcAddress(hDLL, "NET_DVR_SetStandardDataCallBack"); if (installFunc) { return installFunc(lRealHandle, DataCallBack, (DWORD)(*refnum)); } FreeLibrary(hDLL); } return FALSE; } Quote Link to comment
Rolf Kalbermatter Posted June 2, 2022 Report Share Posted June 2, 2022 10 minutes ago, alvise said: This is how it looked when you first posted the code (referring to the code dadreamer shared). Yes I know! But I give up here. I'm probably too old to be able to deal with this. Quote Link to comment
ShaunR Posted June 2, 2022 Report Share Posted June 2, 2022 (edited) 1 hour ago, Rolf Kalbermatter said: 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 think that's a bit harsh. He's a programmer, not a developer. What's the difference? A programmer writes functions that they are told to write. A developer writes solutions. A team will usually have at least one developer designing the solution and producing functions to give to programmers to implement. the problem with these teams is that the programmers get blinkered into "follow ze orders" instead of thinking for themselves. When you look at the LabVIEW Architects exam, you are developing the solution and decompose it into VI's which are often non-functioning. Those functions are implemented by programmers. You (personally) can do both (a one-stop-shop for solutions) but the transition from a programmer to developer is a slow process of growth and mind-set. However. Can you imagine the carnage of responses and "assistance" if this was a Linux forum? Edited June 2, 2022 by ShaunR Quote Link to comment
alvise Posted June 2, 2022 Author Report Share Posted June 2, 2022 Sometimes there are situations where I am torn between doing it right or not. For example: At first I thought the uninstallcalback had to be created in labview because I read a post of yours that talked about creating "uninstallcalback.vi". Then I realized after reading a post you wrote that it is not possible to create it only in labview. Because you said I should use ''not A Refnum''. I interpreted this as ''not A refnum'' should have an equivalent in C++ code. I struggled to create and export a function called unInstallStandardCallback. but I was torn between whether I was doing it exactly right or not. Then I deduced that in order to use ''not A refnum'', a ''Not A refnum'' should be sent to the parameters of the InstallStandardCallback function created without making any changes in the c++ code. Because if no reference is sent, ''return installFunc(lRealHandle, NULL, 0); I interpreted the '' will work and set the values to zero and that way the callback will be removed. I tend to be wrong in this inference, although it is still unclear. -It's a bit difficult for me to create this Example, but I have to do it because I have no other alternative. So I need to do some good research and testing to better understand the situation. After reading what you wrote, I guess it won't be easy to create a VI to parse MPEG4 or H.264 data. I can't insist that anyone help with this. I have no right to waste anyone's time because there are so many situations that I don't understand that I'm between 2 programming languages and there are some problems in both languages that I don't understand. An extra broad topic, callbacks and tokens, I'm sure is no ordinary university issue.I'm not a university student either.I'm a casual person struggling to create a sample application that can only use labview.I am not really a Developer, just eager to learn programming. Thank you everyone for your help. Quote Link to comment
Neil Pate Posted June 2, 2022 Report Share Posted June 2, 2022 @alvise, the problem is your are trying to fly, hypersonically, before you can crawl. Interacting with a DLL as you have been shown to do has heaps of pitfalls. I would recommend you forget about your video stream for now and just make a super simple example that strips everything to its very basics. Quote Link to comment
dadreamer Posted June 2, 2022 Report Share Posted June 2, 2022 @alvise Leaving the lyrics behind, so you got the DLL compiled along with the changes proposed by Rolf? If yes, have you made UninstallCallback VI based on InstallCallback VI with minor changes, proposed by Rolf? If yes, did you put that new VI inside your program and run the test playback again? Quote Link to comment
alvise Posted June 2, 2022 Author Report Share Posted June 2, 2022 Neil Pate You are right But the problem is that simple piece of code may not be the problem. The problem arises when the code starts to get complicated. My main problem is concepts and topics that I am unfamiliar with, so I'm starting from the basics again. dadreamer The question is, what code do I need to compile as Rolfk suggested? Ok, I will go step by step. Step 1: Finally, I changed it as follows and compiled it again. and I used this in Preview CAM.vi. But still the problem persists. If that's true, let's discuss its use in LabVIEW, if not, I'll try to get the C code working first. - To begin with, is the following code correct? #include "extcode.h" #include "hosttype.h" #include "HCNetSDK.h" #define LibAPI(retval) __declspec(dllexport) EXTERNC retval __cdecl #define Callback(retval) __declspec(dllexport) EXTERNC retval __stdcall // Define LabVIEW specific datatypes to pass data as event // This assumes that the LabVIEW event datatype is a cluster containing following elements in exactly that order!!! // cluster // int32 contains the current live view handle // uInt32 contains the dwDataType (NET_DVR_SYSHEAD, NET_DVR_STD_VIDEODATA, NET_DVR_STD_AUDIODATA, NET_DVR_PRIVATE_DATA, // or others as documented in the NET_DVR_SetStandardDataCallBack() function // array of uInt8 contains the actual byte stream data #include "lv_prolog.h" typedef struct { int32_t size; uint8_t elm[1]; } LVByteArrayRec, * LVByteArrayPtr, ** LVByteArrayHdl;// ** LVByteArrayHdl;pointer to a pointer typedef struct { LONG realHandle; DWORD dataType; LVByteArrayHdl handle; } LVEventData; #include "lv_epilog.h" extern "C" __declspec(dllexport) void __cdecl SetCbState(LVBoolean * state); // where Standard Boolean type case is set: typedef uInt8 LVBoolean LVBoolean cbState = LVBooleanFalse; // always set to false on startup extern "C" __declspec(dllexport) void __cdecl SetCbState(LVBoolean * state) { cbState = *state; } 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)); } FreeLibrary(hDLL); } return FALSE; } Quote Link to comment
dadreamer Posted June 2, 2022 Report Share Posted June 2, 2022 (edited) 15 minutes ago, alvise said: - To begin with, is the following code correct? No! You have to change this line: { return installFunc(lRealHandle, DataCallBack, (DWORD)(*refnum)); } to these lines: { if (refnum && *refnum) return installFunc(lRealHandle, DataCallBack, (DWORD)(*refnum)); else return installFunc(lRealHandle, NULL, 0)); } And that's all! Do not touch anything else in the code. Rebuild the DLL then. Edited June 2, 2022 by dadreamer Quote Link to comment
ShaunR Posted June 2, 2022 Report Share Posted June 2, 2022 44 minutes ago, alvise said: My main problem is concepts and topics that I am unfamiliar with Nope. The main problem is you are "learning" by doing something that 98% of the LabVIEW community couldn't do. I'm nowhere near as capable as Rolf or dadreamer on this stuff and if a client had asked me to do it I would have "declined to quote". Quote Link to comment
alvise Posted June 2, 2022 Author Report Share Posted June 2, 2022 (edited) dadreamer Ok. The first line here is the code. -I compiled this code and replaced the dll file with the old dll file and added it to LabVIEW. if ok iIill move on to how i implemented it in Labview. I have added as in the photo below. Is there a problem here? 1- 2- Edited June 2, 2022 by alvise Quote Link to comment
alvise Posted June 2, 2022 Author Report Share Posted June 2, 2022 (edited) 24 minutes ago, ShaunR said: Nope. The main problem is you are "learning" by doing something that 98% of the LabVIEW community couldn't do. I'm nowhere near as capable as Rolf or dadreamer on this stuff and if a client had asked me to do it I would have "declined to quote". 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" Edited June 2, 2022 by alvise Quote Link to comment
Neil Pate Posted June 2, 2022 Report Share Posted June 2, 2022 He means it is difficult enough that it would be hard to give a fixed price without padding it by 300% and then looking stupid when he presented the quote. Quote Link to comment
Rolf Kalbermatter Posted June 2, 2022 Report Share Posted June 2, 2022 (edited) 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. Edited June 2, 2022 by Rolf Kalbermatter Quote Link to comment
dadreamer Posted June 2, 2022 Report Share Posted June 2, 2022 (edited) 1 hour ago, alvise said: Is there a problem here? Seems more or less ok. But better to place UninstallCallback CLFN in the "stop" frame as you install the callback in the "start" frame. I also assume you're passing NotARefnum as Adapt to Type -> Handles by Values with cdecl convention. Wait, I've just thought it would be good to have an organized dataflow in both "start" and "stop" frames. In "start" first do the event handling (registration etc.), then call Start.vi, then install the callback. In "stop" do in the opposite order. You may easily set the dataflow order with two ways: either with brown error wire or with Sequence Structure. There's a State Machine pattern as well, but I feel you're not ready to remake all the program right now. Edited June 2, 2022 by dadreamer Quote Link to comment
Rolf Kalbermatter Posted June 2, 2022 Report Share Posted June 2, 2022 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! Quote Link to comment
alvise Posted June 2, 2022 Author Report Share Posted June 2, 2022 48 minutes ago, dadreamer said: Seems more or less ok. But better to place UninstallCallback CLFN in the "stop" frame as you install the callback in the "start" frame. I also assume you're passing NotARefnum as Adapt to Type -> Handles by Values with cdecl convention. Wait, I've just thought it would be good to have an organized dataflow in both "start" and "stop" frames. In "start" first do the event handling (registration etc.), then call Start.vi, then install the callback. In "stop" do in the opposite order. You may easily set the dataflow order with two ways: either with brown error wire or with Sequence Structure. There's a State Machine pattern as well, but I feel you're not ready to remake all the program right now. I created something like below, but I followed exactly what you said, maybe there are faulty points. If ok, I will proceed accordingly. Preview CAM-7.vi Quote Link to comment
dadreamer Posted June 2, 2022 Report Share Posted June 2, 2022 For "stop" frame: place Stop.vi between the CLFN and Unregister node. And as you are creating the event before the loop (Create User Event node), you should destroy it also after the loop. Quote Link to comment
ShaunR Posted June 2, 2022 Report Share Posted June 2, 2022 (edited) 1 hour ago, Rolf Kalbermatter said: gentle healers make stinking wounds I suppose the nearest equivalent would be "Cruel to be kind"? We all know where it's at. But. Look at it this way. You and dadreamer help him get a callback working. He then get's to the decoding.. This thread already shows, not only how but the path of, implementing callbacks. That's where we are now and is useful to the community (certainly for me, at least). Going forwards maybe, just maybe, someone already has a decoder or suggests using ffmpg or some other way forwards. Maybe someone, thinks it would be useful to help over the next hurdle to get a decoder. Maybe alvise goes ahead and creates one. don't tell me that you wouldn't like that, eh? I see no downsides here. I see potential and a willingness to succeed-exasperating as that may seem at this point. Edited June 2, 2022 by ShaunR Quote Link to comment
alvise Posted June 2, 2022 Author Report Share Posted June 2, 2022 Neil and Rolf Thanks for the explanation. I have noticed before that it is difficult to write some programs in Labview. I have been working with LabVIEW for a long time because Labview is a language with some advantages. My command of other languages was not that good and was forgotten. 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. Quote Link to comment
Rolf Kalbermatter Posted June 2, 2022 Report Share Posted June 2, 2022 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. Quote Link to comment
alvise Posted June 2, 2022 Author Report Share Posted June 2, 2022 1 hour ago, dadreamer said: For "stop" frame: place Stop.vi between the CLFN and Unregister node. And as you are creating the event before the loop (Create User Event node), you should destroy it also after the loop. Here is the VI below where I did what you said. Finally, this way everything should work properly, right? Preview CAM-8.vi Quote Link to comment
dadreamer Posted June 2, 2022 Report Share Posted June 2, 2022 10 minutes ago, alvise said: Finally, this way everything should work properly, right? Looks pretty much like it should. 🙂 Quote Link to comment
alvise Posted June 2, 2022 Author Report Share Posted June 2, 2022 I'm running the VI but still the data can sometimes be read and sometimes not. So the situation I mentioned before still applies. Quote Link to comment
dadreamer Posted June 2, 2022 Report Share Posted June 2, 2022 Now you're about to debug this everything. It's going to be painful a lot! Because there are dozens of things, which could go wrong. First I would start with debugging the DLL by inserting a debug output (like DbgPrintf call or console cout) after each function, that is able to produce errors. But I'm afraid if we go this route, this thread is going to reach 50 pages, if not more. Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.