Jump to content

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


Recommended Posts

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 by Rolf Kalbermatter
Link to comment

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;
}

 

Link to comment
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? :lol:

Edited by ShaunR
Link to comment

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.

Link to comment

@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.

 

Link to comment

@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?

Link to comment

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;
}

 

 

 

Link to comment
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 by dadreamer
Link to comment
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".

Link to comment

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-image.png.40f8f91a221219820a7f3edf6ea12ce2.png

 

2-image.png.fc99da0ef860fcbb8e609e73da68e2a6.png

Edited by alvise
Link to comment

 

 

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 by alvise
Link to comment
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 by Rolf Kalbermatter
Link to comment
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 by dadreamer
Link to comment
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!

Link to comment
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

Link to comment
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 by ShaunR
Link to comment

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.

Link to comment
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.

Link to comment
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

Link to comment

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. :unsure:

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.