12-08-2016 04:11 AM
Hi,
I'm using CVI 2015 and DAQmx 15.1.1.
When I use DAQmx functions from user DLL that created by CVI,
there is Mutex handle leak even if after unload that.
For example, WinDbg reports handle leaks on name start with
"National Instruments LabWindows/CVI Data Acquisition MX Library".
Do I need to call some function to clean up this resource?
This is some of debugger trace:
Handle = 0x00000594 - OPEN Thread ID = 0x00000704, Process ID = 0x0000251c 0xc30cd83a: +0xc30cd83a 0xc2d5b193: +0xc2d5b193 0x43fa6374: +0x43fa6374 0x6a46bf87: +0x6a46bf87 0x6a466ea5: +0x6a466ea5 0x6a451cf7: +0x6a451cf7 0x6a47bfa1: +0x6a47bfa1 0x6a46cbb0: +0x6a46cbb0 0x43f91791: +0x43f91791 0x43fc7b28: +0x43fc7b28 0x43f78d6e: +0x43f78d6e 0x77ebe7ac: ntdll!NtCreateMutant+0x0000000c 0x74a8b7bb: KERNELBASE!CreateMutexExW+0x000000cb 0x74ab1c95: KERNELBASE!CreateMutexA+0x00000075 0x02af2188: <Unloaded_DllProject.dll>+0x00002188 -------------------------------------- Handle = 0x000005d8 - OPEN Thread ID = 0x00000704, Process ID = 0x0000251c 0xc30cd83a: +0xc30cd83a 0xc2d5b193: +0xc2d5b193 0x43fa6374: +0x43fa6374 0x6a46bf87: +0x6a46bf87 0x6a466ea5: +0x6a466ea5 0x6a451cf7: +0x6a451cf7 0x6a47bfa1: +0x6a47bfa1 0x6a46cbb0: +0x6a46cbb0 0x43f91791: +0x43f91791 0x43fc7b28: +0x43fc7b28 0x43f78d6e: +0x43f78d6e 0x77ebe7ac: ntdll!NtCreateMutant+0x0000000c 0x74a8b7bb: KERNELBASE!CreateMutexExW+0x000000cb 0x74ab1c95: KERNELBASE!CreateMutexA+0x00000075 0x02af2188: <Unloaded_DllProject.dll>+0x00002188 -------------------------------------- Displayed 0x66 stack traces for outstanding handles opened since the previous snapshot. 0:011> !handle 5d8 f Handle 5d8 Type Mutant Attributes 0 GrantedAccess 0x1f0001: Delete,ReadControl,WriteDac,WriteOwner,Synch QueryState HandleCount 102 PointerCount 3342236 Name \Sessions\1\BaseNamedObjects\National Instruments LabWindows/CVI Data Acquisition MX Library 9500 Object Specific Information Mutex is Free
12-15-2016 11:12 PM
This is some hack for DLL generated by CVI 2015, call this inside your DLL before unloaded(32bit).
typedef void (__cdecl *CleanFunc)(int); static void cleanUp(void) { unsigned int *p; CleanFunc func; p = (unsigned int *)DAQmxCreateTask; func = (CleanFunc)(p - (0x74e0 / 4)); func(4); }
01-22-2017 07:31 AM
Hello,
Wonderful hack, maybe place it in the DLL entry point for Process/Thread Detach?
If you run one of the DAQmx ANSI C examples, do you find this to still be the case? If so, I would contact your local NI support to inform them of the mutex leak and your findings.
Best regards,
Ed
01-23-2017 01:46 AM
I'm using it in DllMain.
I don't think DAQmx ANSI C examples show handle problem because they are all single executable file example. I'll attach sample source files for this case.
Build first source as DLL file name as SampleDAQLibrary.dll and check them with second source. If you did not call cleanFunc in DllMain detach case, you can see increased handle count in task manager.
#include <stdio.h> #include <NIDAQmx.h> #include <Windows.h> #include <cvirte.h> /*****************************************************************************/ typedef void (__cdecl *CleanFunc)(int); static void cleanUp(void) { unsigned int *p; CleanFunc func; p = (unsigned int *)DAQmxCreateTask; func = (CleanFunc)(p - (0x74e0 / 4)); func(4); } __declspec(dllexport) void TestAnalogIn(void) { TaskHandle handle; double buff[256]; int arraySize, readSize; if (DAQmxCreateTask("", &handle) != 0) { printf("Error on DAQmxCreateTask\n"); return; } if (DAQmxCreateAIVoltageChan(handle, "Dev1/ai0", "", DAQmx_Val_RSE, -5, 5, DAQmx_Val_Volts, NULL) != 0) { printf("Error on DAQmxCreateAIVoltageChan\n"); goto exit; } arraySize = 4; if (DAQmxReadAnalogF64(handle, DAQmx_Val_Auto, 1.0, DAQmx_Val_GroupByChannel, buff, arraySize, &readSize, NULL) != 0) { printf("Error on DAQmxReadAnalogF64\n"); } printf("Read %d samples\n", readSize); exit: if (DAQmxStopTask(handle) != 0) { printf("Error on DAQmxStopTask\n"); } if (DAQmxClearTask(handle) != 0) { printf("Error on DAQmxClearTask\n"); } } /*****************************************************************************/ int __stdcall DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: if (InitCVIRTE (hinstDLL, 0, 0) == 0) { return 0; } break; case DLL_PROCESS_DETACH: CloseCVIRTE(); /* call cleanUp before unloaded */ cleanUp(); break; } return 1; } int __stdcall DllEntryPoint (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { /* Included for compatibility with Borland */ return DllMain (hinstDLL, fdwReason, lpvReserved); }
#include <Windows.h> #include <stdio.h> #include <stdlib.h> /*****************************************************************************/ typedef void (*testFunc)(void); static void test(void) { HMODULE handle; testFunc func; handle = LoadLibrary("SampleDAQLibrary.dll"); if (!handle) { printf("Error on load library\n"); return; } func = (testFunc)GetProcAddress(handle, "TestAnalogIn"); if (!func) { printf("Error on get function address\n"); goto exit; } func(); exit: FreeLibrary(handle); } int main(void) { int i; printf("first prerun\n"); test(); printf("Enter to continue\n"); getchar(); for (i = 0; i < 100; ++i) { printf("run %d\n", i); test(); } printf("End\n"); getchar(); return (0); }