From 1244ca004e23523cd47d3ca17458b877291fb04d Mon Sep 17 00:00:00 2001 From: Chadwick Boulay Date: Sat, 19 Sep 2026 01:53:53 -0400 Subject: [PATCH] pull_sample(string[]): leave output untouched when no sample is available liblsl >= 1.18 (sccn/liblsl#290) no longer allocates strings into the output buffer when lsl_pull_sample_str returns 0.0, so the pointers stay null and Marshal.PtrToStringAnsi would have written null into the caller's array (previously it was an empty string). Only convert when a sample was actually returned, matching the numeric overloads; keep the unconditional lsl_destroy_string loop (null-safe) so older liblsl builds still don't leak. --- LSL.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/LSL.cs b/LSL.cs index bb7137d..6dfc17a 100644 --- a/LSL.cs +++ b/LSL.cs @@ -611,11 +611,16 @@ public double pull_sample(string[] sample, double timeout = LSL.FOREVER) double res = dll.lsl_pull_sample_str(obj, tmp, tmp.Length, timeout, ref ec); LSL.check_error(ec); try { - for (int k = 0; k < tmp.Length; k++) - sample[k] = Marshal.PtrToStringAnsi(tmp[k]); + // liblsl >= 1.18 leaves tmp untouched (all null) when no sample was available (res == 0.0); + // in that case leave the caller's array untouched too, like the numeric overloads do. + if (res != 0.0) + for (int k = 0; k < tmp.Length; k++) + sample[k] = Marshal.PtrToStringAnsi(tmp[k]); } finally { + // lsl_destroy_string is null-safe; always free so older liblsl versions + // (which allocate empty strings on timeout) don't leak. for (int k = 0; k < tmp.Length; k++) dll.lsl_destroy_string(tmp[k]); }