NSClient++ Help (#1) - Developing own DLL (#126) - Message List
I am developing a plugin for Windows systems. It is called via nrpe. It is an executable Windows binary. I want to make it run in NSClient++ as dynamic Link Library. What is the easiest way to do that? How do I have to write my classes, or is there a way to do it without changing my Code, just to implement something like an Interface. In the end I want to call my Plugin with an "inject"-command. Can you tell my what I have to do?
Thanks, reichi
-
Message #329
What language is it you are using?
But in short there is a set of function you expose from your DLL and thats about it, if you look at the source modules/* you can quite easily see exactly how it is done, there is also a bunch of .h files used (for instance nscapi.h) that define the interface. If you develop in C++ there is a bunch of helper code that you can use so you wont have to manage the interface at all.
Notice though that I have some minor changes for 0.4.0 (minor optional additions) and for 0.5.0 there will be some API changes (notably string handling).
Michael Medin
mickem04/11/08 12:22:15 (5 years ago)-
Message #330
I am using C# for development.
reichi04/11/08 14:46:42 (5 years ago)-
Message #331
Then you are "on your own" sort of... but should not be so hard... You need to export (one or more of these) from your DLL (for syntax check the .h file) And I am not sure exactly here this is from the top of my head, can write up better info later on if you want, but this might give you some idea on what to do to get started.
- NSModuleHelperInit
Called to say "hello" to you, the function you get here can be used to "extract" call back functions that you can use to do stuff with the "core".
- NSLoadModule
Used to "boot" your module.
- NSUnloadModule
"un-boot" you mosule (ie. terminate and free-up memory/resources)
- NSGetModuleName
Return a nice name for your module.
- NSGetModuleDescription
Return a nice description for your module.
- NSGetModuleVersion
Return version (never used as of now, but might some day be used :)
- NSHasCommandHandler
Return true if you handle commands
- NSHandleCommand
Handle a command (ie. run a "check")
- NSHasMessageHandler
Return true if you want to handle messages (log data)
- NSHandleMessage
Handle a log message (not needed unless you responded true above)
- NSCommandLineExec
Handle command line execution (ie nsclient++ -noboot <module> ...)
Thats about all you need to do...
MickeM
mickem04/11/08 15:41:26 (5 years ago)-
Message #339
Can you tell me how I have to explain the nsclient++ to use my module with command for instance:
command[my_own_mod]=inject myCommand -H ...
Where do I have to declare myCommand, thats something, I didn't yet understand?!
Thanks, reichi
reichi04/15/08 11:24:32 (5 years ago)-
Message #340
in short: you dont...
If you want to use an external program (ie not an internal plugin) then you just point the way to your executabel/script. If you want (as I understood it) want to develop an internal NSCLient++ module (DLL). Then you build the DLL and add it to the modules/ directory and also under the [modules] section.
After that the "module" will be called (in the same order as the modules section) whenever a command is executed, and the first module that responds with "ok" will end "chain" and return the result to the caller.
Before that your DLL need to respond "true" to NSHasCommandHandler to make your module into a command handler. (there is also a way t register commands with the client via one of the callbacks, but that is not really used as of now, but will be in 0.5.0)
It works like this:
on_boot: ... foreach module in <[modules]> module.NSModuleHelperInit module.NSLoadModule if module.NSHasCommandHandler command_handlers.add(module) ... on_incoming_request: ... foreach module in command_handlers: ret := module.NSHandleCommand if is_valid_return_code(ret): return ret; ... return "unknown command"; ... on_exit: foreach module in <[modules]> module.NSUnloadModule ...in sort of python:ish meta programming :)
Michael Medin
mickem04/15/08 12:27:50 (5 years ago)-
Message #691
Hi
I am working on a CSharp version of a dll. I have some question about type conversion. I NSClient will call the dll, it will expect exactly the same parameter as you defined in the NSC_WRAPPERS_MAIN(). could you tell me how I can declare this in c# ? : extern "C" int NSModuleHelperInit(NSCModuleHelper::lpNSAPILoader f); Also do you have any hint on how to work with TCHAR in C# ? I wanted to stick to C++ but turned out that someone already develop a part of what I want to do in C#.
thanks Franck
franck09/04/08 22:16:13 (5 years ago)-
Message #692
Hi I found how to pass a function as parameter in C# here is what I did. I am not sure it is the right way to do in term of write a c# dll for NSClient. But I will keep you guys posted. If anyone have a hint, tips or snippet. I'd be glad to read it.
franck
franck09/04/08 23:30:25 (5 years ago)-
Message #693
sorry here is what I did. sorry for the spam. it is quite late in France right now. getting tired.
..... namespace NSCSharpHelper {
public delegate void* NSAPICSharpLoader([MarshalAs?(UnmanagedType?.LPTStr)] String buf);
} ...... int NSModuleHelperInit(NSCSharpHelper.NSAPICSharpLoader f);
franck
franck09/04/08 23:33:05 (5 years ago)-
Message #694
I don't really know how to do it in C# but using/writing DLLs from C# ought to be fairly simple and well documented from Microsoft (and the like) Id start by looking at CodeProject?. And especially read up on writing DLL:s and look at "using" the "W32 API" (as it is the same strings and what not).
NSClient++ uses normal DLL:s and the strings are normal Unicode "windows" string. Everything was designed to be "compatible" so it should be possible to write extensions in virtually every language (for windows). An important aspect that may not be obvious is that you need to marshal threading as well as the memory.
I will at some point look into this but I do not have the time at the moment so if you get something up and running feel free to submit it and I shall be happy to create a sample .net plugin.
Michael Medin
mickem09/05/08 06:50:21 (5 years ago)-
Message #696
Hi I am checking out your code and seems like there is no need to handle thread. If I take the example of CheckDisk, it looks like the NSclient++ is handling the thread. Am I right?
franck
franck09/08/08 10:38:27 (5 years ago)-
Message #697
that depends...
if you don't use any variables between calls, most checks dont do that and then it is not a problem. For examples of where I do it you can check the CheckCPU and CheckMemory? and such.
But there is no handling by the core to protect plugins from threadding issues. Requests are executed as they arrive...
Michael Medin
mickem09/08/08 11:00:21 (5 years ago)-
Message #698
ok thanks for following up I may have more questions later on. I am trying to build a small NSCSharpUtil that will help me create easily a dll module in C#. So I guess there are more questions comming soon. I will be glad to share my results with you. thanks for your help. franck
franck09/08/08 11:25:24 (5 years ago)-
Message #710
Hi I have a question about the function NSModuleHelperInit. It is basically a function which takes another one as parameter. My understanding is that NSClient will call NSModuleHelperInit from my dll and pass it a pointer to a function namely NSAPILoader. This is normally just a pointer soI tried to define my function in the c# as follows but it is not working whebn it comes to load the dll in NSClient++. Looks like my definition of NSModuleHelperInit is wrong. here is my c# declaration
public static int NSModuleHelperInit(IntPtr? pointer);
Could you help me on this?
franck09/11/08 11:40:33 (5 years ago)-
Message #713
the forums is not really suitable for deep threading I see :)
Anyways.
NSCLient loads your DLL and calls NSModuleHelperInit. Then it calls NSModuleLoad (or some such).
The parameter for NSModuleHelperInit is not an int pointer it is a function pointer, the function in question can be used to extract an assortment of other function pointers from NSClient to access the core from your plugin.
I am at a conference at the moment so I can dig a little deeper next week if you want...
Michael Medin
anonymous09/11/08 13:24:02 (5 years ago)-
Message #715
sure Any help will be more than welcome. about the threading you can use the flat view option :)
regards
franck09/11/08 13:43:26 (5 years ago)-
Message #716
The defenitions (C++) for your functions BTW:
97 typedef INT (*lpModuleHelperInit)(NSCModuleHelper::lpNSAPILoader f); 98 typedef INT (*lpLoadModule)(); 99 typedef INT (*lpGetName)(TCHAR*,unsigned int); 100 typedef INT (*lpGetDescription)(TCHAR*,unsigned int); 101 typedef INT (*lpGetVersion)(int*,int*,int*); 102 typedef INT (*lpHasCommandHandler)(); 103 typedef INT (*lpHasMessageHandler)(); 104 typedef NSCAPI::nagiosReturn (*lpHandleCommand)(const TCHAR*,const unsigned int, TCHAR**,TCHAR*,unsigned int,TCHAR *,unsigned int); 105 typedef INT (*lpCommandLineExec)(const TCHAR*,const unsigned int,TCHAR**); 106 typedef INT (*lpHandleMessage)(int,const TCHAR*,const int,const TCHAR*); 107 typedef INT (*lpUnLoadModule)();
and all the call backs:
102 // Types for the Callbacks into the main program 103 typedef NSCAPI::errorReturn (*lpNSAPIGetBasePath)(TCHAR*,unsigned int); 104 typedef NSCAPI::errorReturn (*lpNSAPIGetApplicationName)(TCHAR*,unsigned int); 105 typedef NSCAPI::errorReturn (*lpNSAPIGetApplicationVersionStr)(TCHAR*,unsigned int); 106 typedef NSCAPI::errorReturn (*lpNSAPIGetSettingsString)(const TCHAR*,const TCHAR*,const TCHAR*,TCHAR*,unsigned int); 107 typedef NSCAPI::errorReturn (*lpNSAPIGetSettingsInt)(const TCHAR*, const TCHAR*, int); 108 typedef NSCAPI::errorReturn (*lpNSAPIGetSettingsSection)(const TCHAR*, arrayBuffer::arrayBuffer*, unsigned int *); 109 typedef NSCAPI::errorReturn (*lpNSAPIReleaseSettingsSectionBuffer)(arrayBuffer::arrayBuffer*, unsigned int *); 110 typedef void (*lpNSAPIMessage)(int, const TCHAR*, const int, const TCHAR*); 111 typedef NSCAPI::errorReturn (*lpNSAPIStopServer)(void); 112 typedef NSCAPI::errorReturn (*lpNSAPIExit)(void); 113 typedef NSCAPI::nagiosReturn (*lpNSAPIInject)(const TCHAR*, const unsigned int, TCHAR **, TCHAR *, unsigned int, TCHAR *, unsigned int); 114 typedef void* (*lpNSAPILoader)(TCHAR*); 115 typedef NSCAPI::boolReturn (*lpNSAPICheckLogMessages)(int); 116 typedef NSCAPI::errorReturn (*lpNSAPIEncrypt)(unsigned int, const TCHAR*, unsigned int, TCHAR*, unsigned int *); 117 typedef NSCAPI::errorReturn (*lpNSAPIDecrypt)(unsigned int, const TCHAR*, unsigned int, TCHAR*, unsigned int *); 118 typedef NSCAPI::errorReturn (*lpNSAPISetSettingsString)(const TCHAR*, const TCHAR*, const TCHAR*); 119 typedef NSCAPI::errorReturn (*lpNSAPISetSettingsInt)(const TCHAR*, const TCHAR*, int); 120 typedef NSCAPI::errorReturn (*lpNSAPIWriteSettings)(int); 121 typedef NSCAPI::errorReturn (*lpNSAPIReadSettings)(int); 122 typedef NSCAPI::errorReturn (*lpNSAPIRehash)(int); 123 typedef NSCAPI::errorReturn (*lpNSAPIDescribeCommand)(const TCHAR*,TCHAR*,unsigned int); 124 typedef NSCAPI::errorReturn (*lpNSAPIGetAllCommandNames)(arrayBuffer::arrayBuffer*, unsigned int *); 125 typedef NSCAPI::errorReturn (*lpNSAPIReleaseAllCommandNamessBuffer)(arrayBuffer::arrayBuffer*, unsigned int *); 126 typedef NSCAPI::errorReturn (*lpNSAPIRegisterCommand)(const TCHAR*,const TCHAR*); 127
MickeM
anonymous09/11/08 13:57:21 (5 years ago)-
Message #717
Hi Micheal. I was aware about this. my C# class is implementing or handling these functions. the only problem is handling a function as parameter inside my c#. I am still digging.
Franck
franck09/11/08 15:27:01 (5 years ago)-
Message #732
Hi Micheal. I solved this issue by twicking a bit your NSplugin. basically passing the function as variable end up playing with the ret and the sfp of the init function. The solution I found is to create a proxy dll in c++ that is able to pass out some static variable to my C# dll.
I am still dealing with other bug. once everything is fixed. I will share my solution with you.
regards.
franck09/15/08 17:42:53 (5 years ago)-
Message #733
I did some quick testing and you (apparently) have to write a wrapper in managed C++, from which you can use the C# code...
The wrapper will be about 20-30 lines so it should not be too bad I think... but I have yet to figure out how to put them both in the same "DLL"...
MickeM
mickem09/15/08 19:33:48 (5 years ago) -
Message #734
there is a very basic but functional "C# plugin" with a managed C++ wrapper in the SVN now.
The "main" problem is that I don't exactly know how to get rid of having 2 DLL:s (if at all possible) Also note that the "C# DLL" has to be in the same directory as the *exe* (not the modules directory).
MickeM
mickem09/15/08 21:05:15 (5 years ago)-
Message #735
Hi thanks for commiting this example. looks like if I want to create two modules, I have to create two wrappers. let me know if my solution is better. Basically the idea was to create a dll(proxy) that we loaded first. in the NSPlugin.cpp I added this. Obviously it is nasty code for now. but the Idea is to ask each dll wich type they are and if it is a proxy. I just cached the function for every dll written in c#.
of course i am marshaling my c# in order to deal with unmanaged types.
/*at this point we have to handle C# library we got to detect which type is it to handle CSharp in a different way */
/*a bit nasty for now just to make sure it is working */
typedef int (*lpwhichTypeAreYou) (); lpwhichTypeAreYou fwhichTypeAreYou = (lpwhichTypeAreYou)GetProcAddress?(hModule_, "NSwhichTypeAreYou"); int myType = fwhichTypeAreYou(); proxy if(myType ==0){
try { fModuleHelperInit = (lpModuleHelperInit)GetProcAddress?(hModule_, "NSModuleHelperInitProxy"); fModuleHelperInit(NSAPILoader); } catch (...) {
throw NSPluginException(file_, _T("Unhandled exception in getDescription."));
}
} CSharp else if(myType ==1){
try {
typedef void (*lpCSharpModuleHelperInit) ();
lpCSharpModuleHelperInit fCSharpModuleHelperInit = (lpCSharpModuleHelperInit)GetProcAddress?(hModule_, "NSCSharpModuleHelperInit"); fCSharpModuleHelperInit(); } catch (...) {
throw NSPluginException(file_, _T("Unhandled exception in getDescription."));
}
} c++ else {
fModuleHelperInit = (lpModuleHelperInit)GetProcAddress?(hModule_, "NSModuleHelperInit"); if (!fModuleHelperInit)
throw NSPluginException(file_, _T("Could not load NSModuleHelperInit"));
try {
NSCModuleHelper::lpNSAPILoader NSAPILoaderAddr=&NSAPILoader; fModuleHelperInit(NSAPILoader);
} catch (...) {
throw NSPluginException(file_, _T("Unhandled exception in getDescription."));
}
}
franck09/15/08 21:56:22 (5 years ago)-
Message #736
Not sure I follow what you mean.
As far as I can tell it is impossible to create "proper" DLL:s in C# (apparently you need Managed C++ for that). So if you have a solution to that it would be nice. But I dont really see how the above would solve that? But with the above code you don't get any "callbacks" which would be sort of a bad thing (Since they are quite handy). But I think it is fairly simple to get marshal the function pointer to C#.
MickeM
mickem09/15/08 22:07:09 (5 years ago)-
Message #737
yeah I didn't want to copy and paste everything. basically in the proxy dll I save the callback function as static. So in my c# i load this proxy dll and I can get all the call back pointer since they are static.
This suppose I have implemented functions in the proxy which allow me to get pointer to those function. I have to admit that This is may be too complicated. but with this I have one dll which save pointers for every c#. hope this is clear.
franck09/15/08 22:22:29 (5 years ago)-
Message #738
it would (in that case) make more sense to write a managed "C# plugin" that loads C# plugins, but since it (AFAICT) requires compile time knowledge of the implementation I don't really see the benefit, but I suppose it would be a way to go.
Anyways, I am not a .net developer so I honestly don't really know. The "reason" I did things the way I did them is that all "functions" are handled by the same wrapper layer I have for the C++ plugins so it was less code to write :)
Michael Medin
mickem09/15/08 22:34:20 (5 years ago)-
Message #739
that's right. thanks for your help. I am going to use your example for sure. I have some idea of how to merge our views. but how can I share code with you?
franck09/15/08 22:48:41 (5 years ago)-
Message #740
well, either you can attach it here or you can email or some such, I might possibly figure out how to setup commit access for the SVN if you want to write a "proper plugin" or some such...
MickeM
mickem09/16/08 06:41:07 (5 years ago)-
Message #741
ok I will start by attaching files first. also I got a problem on your last commit. in the file trayIcon.cpp the function ChangeWindowMessageFilter?. got this. error C2373: 'ChangeWindowMessageFilter?' : redefinition; different type modifiers looks like you are redefining a function from winuser.h that you may not??
any hint?
franck09/16/08 11:58:20 (5 years ago)-
Message #742
uhmm, you are most likely using a newer version of windows API then I... you can probably add a #define around it to get away from the problem or perhaps better rename the function...
I have (sometimes) added an underscore to function names to get away from the problem, but might have forgotten in this instance...
BTW; what branch are you on? (current version is stable, but I am in the process of backporting all those changes to the trunc so in a week or so the trunc will again be the "development"...
MickeM
mickem09/16/08 12:01:26 (5 years ago)-
Message #743
I used the branch stable https://druss.medin.name/trac/nscp/browser/branches/stable. Since it is where you commited the code. I renamed the function by by adding and underscore like this : ChangeWindowMessageFilter_ and it is compiling find. thanks
franck09/16/08 12:09:01 (5 years ago)-
Message #744
thats what I usually do... feel free to submit a patch and I shall apply it tonight...
MickeM
mickem09/16/08 12:14:14 (5 years ago)-
Message #746
ok I mailed it. one more issue is the call of createEnvironmentBlock. looks like you may declare the definition of the function? it is declare in the userenv.h
1>NSClient++.obj : error LNK2019: unresolved external symbol impCreateEnvironmentBlock@12 referenced in function "public: static bool cdecl tray_starter::startTrayHelper(unsigned long,class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> >,class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> >,bool)" (?startTrayHelper@tray_starter@@SA_NKV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@0_N@Z)
franck09/16/08 18:10:32 (5 years ago)-
Message #747
sorry for spamming now i feel stupid :) did not add the userenv.lib in the project. May be something to add in the project properties in the trunk!!
franck
franck09/16/08 19:51:11 (5 years ago)-
Message #753
Hi. I got something working. I added a basic "c# federator" which is basically collecting all Managed dll into one. I reused your example for that. basically people could just develop the c# code using the interface I will give them. And the c# federator will append all functions as if it was its own. Did you got the mail i sent you? if yes I can send you my project.
Note I tried to merge both c++ and c# dll without success. Finally I guess it is impossible in our case if we still want to use the unmanaged c++ wrapper.
If I have time I will try to continue debugging what I started with i.e. the c# marshalled dll to plug directly.
franck
franck09/18/08 23:06:38 (5 years ago)-
Message #754
Dont know what a C# fedarator is, but the "overall plan" I have for this is to:
- define a C# interface
- Write a Managed C++ module that "loads other modules" (based on the interface)
- Have under [modules] a ".net loader"
- add a new [.net modules] section with pure C# modules that are loaded "via" the .net loader module.
And I got the email a while back so feel free to send it over.
MickeM
mickem09/19/08 06:33:39 (5 years ago)-
Message #755
THat is pretty much what I did. The "federator" is a name i choose for the ".net loader". may be the word federate make more sence in french :).
I am about to send you the mail. Let me know what you think.
anonymous09/19/08 07:57:13 (5 years ago)-
Message #762
looks like the mail did not work. I added modules projects in an svn. here is the link: https://opensvn.csie.org/viewcvs.cgi/NSClient%2B%2B/modules/modules/?root=middleware
franck
franck09/19/08 17:30:10 (5 years ago)-
Message #763
Hi I just correct a bug inside your SampleManagedPlugin?.h:
line 53 was: List<String> args; I changed it to: List<String> args = gcnew List<String>();
now command arguments are working regards
franck09/22/08 11:01:49 (5 years ago)-
Message #879
Hey guys. This thread seams to cope with pretty much the topic I am up to.
As I understand the whole topic: 1. you need the managed C++ DLL 2. write the actual C# Plugin.
I read about the C# Sharp Interface/francks "federator". Any chance you might put the interface or the federator on the svn? That'd really help...
Cheers,
Max
Max11/12/08 15:45:35 (5 years ago)-
Message #880
Hi. I have enable the anonymous check out on my SVN. http://opensvn.csie.org/middleware/
under NSClient++. I will load my code tonight since open svn is having some problem. Did not have any problem so far with it. All my C# are getting load and "federate" properly.
Regards Franck
franck11/12/08 15:58:01 (5 years ago)-
Message #881
Great! Thanks for the quick reply.
I might be (most likely i will) ask some further questions :)
Max11/12/08 16:13:00 (5 years ago)-
Message #884
Hey franck,
NSModuleInterface is the actual Interface which has to be used to create the C# Plugin + I need the ManagedPluginFederator? but do I also need the "SampleManagedPlugin?" (the c++ one)?
Could you explain the process of creating a plugin with your federator?
Max11/19/08 15:17:41 (5 years ago)-
Message #885
My Process:
- build Federator, output (dll) -> SampleManagedPlugin?\lib
- Build ManagedPlugin?, modify SampleManagedPlugin?.c++ to use lib/ManagedPluginFederator.dll. But I'm actually getting:
1>SampleManagedPlugin?.cpp 1>.\SampleManagedPlugin?.cpp(11) : error C2660: 'SampleManagedPlugin?<target>::loadModule': Funktion akzeptiert keine 1 Argumente (function does not accept 1 argument) 1> with 1> [ 1> target=ManagedPluginFederator::Federator 1> ]
- build my plugin, put it in modules\managedModules and GO.
Am I right?
Cheers, Max.
max11/19/08 16:17:10 (5 years ago)-
Message #886
Ohh sorry you changed that already I was in the middle of something so I read your question quite quickly. For me this error is like you have changed the federator and added and argument to the loadmodule function which should not have any.
Note that you may want to copy the federator where your exe is. So I will add a forth step to what you have already.
- copy ManagedPluginFederator?.dll in the same directory as NSClient.exe
regards Franck
franck11/19/08 16:45:08 (5 years ago)-
Message #887
no, i didnt change the federator. This error occurs when compiling the SampleManagedPlugin?. The affected lines are:
SampleManagedPlugin?<ManagedPluginFederator::Federator> gPluginImpl; NSC_WRAPPERS_MAIN_DEF(gPluginImpl);
anonymous11/19/08 17:21:00 (5 years ago)-
Message #888
ok looks like you may go into your solution explorer in the project SampleManagedPlugin? in visual studio and make sure you have included the header file NSCHelper.h. At least that the path is right.
franck11/19/08 17:39:51 (5 years ago)-
Message #902
Had anyone tried to install the solution? I want to package the whole thing now and run it as a service. Just realised that managed modules are getting skipped onload. So when I do NSclient++ /test everything works find but if I do NSClient++ /install then NSclient++ /start , no managed commands is available. I check the log file. the printf I did in the c++ module when it gets load is not showing up when I start the service. But it is showing up when I run it in test mode.
Any Ideas?
thanks
franck12/01/08 18:11:04 (5 years ago)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-








