12-13-2024 10:25 AM
I am trying to get a callback working in LabVIEW using the PostLVUserEvent. I have it working great using an integer but am having an issue with strings. I have 2 versions that I am working with. The first is the Test LV Callback2.vi. This registers and fires the event all in the same function. This is working and I get the string from the dll back into LabVIEW. The second example is Test LV Callback.vi. This separates things into 2 functions. 1 function to register the LV event. Then a second function to fire the event. This works fine for the integer, but the string it does not. I do not get an event inside LabVIEW. I have tried many different ways and debugging techniques and have not been able to figure out why this is not working. Does anyone have some inisght into what is going on here and some things I can try.
I have attached the code (LV version 2021) The VIs are saved as 2021 but I am using this in LV 2024. For ease of viewing below is the code:
header file:
#pragma once
#include "extcode.h"
// Function prototypes
extern "C" __declspec(dllexport) void RegisterIntCallback(LVUserEventRef* eventRef);
extern "C" __declspec(dllexport) void RegisterStringCallback(LVUserEventRef* eventRef);
extern "C" __declspec(dllexport) void FireIntCallback(); // No parameter
extern "C" __declspec(dllexport) void FireStringCallback(); // No parameter
extern "C" __declspec(dllexport) void RegisterAndFireStringCallback(LVUserEventRef* eventRef);
C++ code
#include "pch.h"
#include "LVCallbackTest.h"
#include "extcode.h"
#include <cstdlib> // For rand()
#include <ctime> // For time()
static LVUserEventRef intEventRef = NULL;
static LVUserEventRef stringEventRef = NULL;
extern "C" __declspec(dllexport) void RegisterIntCallback(LVUserEventRef* eventRef) {
intEventRef = *eventRef;
}
extern "C" __declspec(dllexport) void RegisterStringCallback(LVUserEventRef* eventRef) {
if (eventRef != nullptr) {
stringEventRef = *eventRef; // Dereference and store the value
}
else {
stringEventRef = NULL; // Handle null pointer input
}
}
extern "C" __declspec(dllexport) void FireIntCallback() {
if (intEventRef != NULL) {
std::srand(static_cast<unsigned int>(std::time(0))); // Seed the random number generator
int randomValue = std::rand(); // Generate a random number
PostLVUserEvent(intEventRef, &randomValue);
}
}
extern "C" __declspec(dllexport) void FireStringCallback() {
if (stringEventRef != NULL) {
// Create a LabVIEW string handle
LStrHandle lvStringHandle = (LStrHandle)DSNewHandle(sizeof(int32) + strlen("Hello from the DLL!") * sizeof(uChar));
if (lvStringHandle != NULL) {
// Set the length of the string
LStrLen(*lvStringHandle) = strlen("Hello from the DLL!");
// Copy the string into the handle
memcpy(LStrBuf(*lvStringHandle), "Hello from the DLL!", strlen("Hello from the DLL!"));
// Post the event
PostLVUserEvent(stringEventRef, (void*)&lvStringHandle);
// Dispose of the handle
DSDisposeHandle(lvStringHandle);
}
}
}
extern "C" __declspec(dllexport) void RegisterAndFireStringCallback(LVUserEventRef* eventRef) {
// Register the event
LVUserEventRef stringEventRef = *eventRef;
// Create a LabVIEW string handle
LStrHandle lvStringHandle = (LStrHandle)DSNewHandle(sizeof(int32) + strlen("Hello from the DLL!") * sizeof(uChar));
if (lvStringHandle != NULL) {
// Set the length of the string
LStrLen(*lvStringHandle) = strlen("Hello from the DLL!");
// Copy the string into the handle
memcpy(LStrBuf(*lvStringHandle), "Hello from the DLL!", strlen("Hello from the DLL!"));
// Post the event
PostLVUserEvent(stringEventRef, (void *)&lvStringHandle);
// Dispose of the handle
DSDisposeHandle(lvStringHandle);
}
}
Solved! Go to Solution.
12-13-2024 10:46 AM
You are calling RegisterStringCallback without arguments instead of calling FireStringCallback. Change the function name in the last call library node.
12-13-2024 11:15 AM
Wow that was much easier than I thought! Thank you for finding that. This code now works and if I use this example in my larger application it is also now working (The larger application had a bad way of handling the strings that I was sure I fixed in this simple example).
So let this example be a good one for people to reference if they need to use the PostLVUserEvent. I had a hard time finding example code, so hopefully this helps someone out.
12-13-2024 12:50 PM
Just today I added a function to a DLL that I call from LabVIEW. Copied a different caller VI for the function, changed the cluster definition and promptly forgot to change the name. It didn’t crash and even sort of made sense, which was really weird for an array with nested clusters. Took me way too long to notice..
Anyway, it’s always good to get more examples for PostLVUserEvent. John Medland has a lot of good content and references on his GitLab site: https://gitlab.com/serenial
I think you are not allocating enough memory for 64 bit Windows, you need to take alignment into account. Here is a link for that: https://lavag.org/topic/21269-dll-linked-list-to-array-of-strings/?do=findComment&comment=129666
(Which I also got from John’s repo)
12-15-2024 05:51 PM - edited 12-15-2024 05:52 PM
@cordm wrote:
I think you are not allocating enough memory for 64 bit Windows, you need to take alignment into account. Here is a link for that: https://lavag.org/topic/21269-dll-linked-list-to-array-of-strings/?do=findComment&comment=129666(Which I also got from John’s repo)
A string handle is a byte array and bytes have always an alignment of 1 byte. So there is no filler needed. But it is indeed a smart idea to use NumericArrayResize() instead of DSNewHandle()/DSHewHdClr(). NumericArrayResize takes both the dimension int32’s and possible alignment requirements into account. For String handles you simply specify as type uB and 1 dimension.