Issue using .dll in Program vs Service
-
I need some assistance in tracking down why my code will not run as a service. I have been hacking at this for over two weeks on and off with no real progress. I need to use an old dll to access data. When I call it from a console app, it works just fine. Here is a snip of the code that works... [DllImport("C:\\Program Files\\Omaha_Steaks_NUCAS_Reader\\libhab2.dll")] public static extern int HABRSCI(string RSCI1, string RSCI2, string RSCI3, string RSCI4, string RSCI5); static void Main() { try { //Create Variables for Host Access string string resbuff = new string(' ', 80); string reascode = "0000"; HABRSCI("0000", "01", "nucas.txt\0", resbuff, reascode); } catch (Exception ex) { string zzzz = ex.Message; } } I rewrote as a Service, everything works except the dll call, it just acts as it was never called at all. Here are some snips from my Service code.... //Set up usage of the .dll for Host Access [DllImport("C:\\Program Files\\OS_NUCAS_Reader\\libhab2.dll",EntryPoint = "HABRSCI")] public static extern int HABRSCI(string RSCI1, string RSCI2, string RSCI3, string RSCI4, string RSCI5); and later, inside of a timer event.. try { //Create Variables for Host Access string string resbuff = new string(' ', 80); string reascode = "0000"; HABRSCI("0000", "01", "nucas.txt\0", resbuff, reascode); } catch (Exception ex) { } This is just ignored!!! I know the code executes around it, I have even added lines to write to the Event log just before and after the call. If it works, a text file is created and a records is added to a log file maintained by the dll. none of these are happening so it apears that it ignores the call to the dll when running as Service. Any assistance would be much appreciated. Thanks, Don
-
I need some assistance in tracking down why my code will not run as a service. I have been hacking at this for over two weeks on and off with no real progress. I need to use an old dll to access data. When I call it from a console app, it works just fine. Here is a snip of the code that works... [DllImport("C:\\Program Files\\Omaha_Steaks_NUCAS_Reader\\libhab2.dll")] public static extern int HABRSCI(string RSCI1, string RSCI2, string RSCI3, string RSCI4, string RSCI5); static void Main() { try { //Create Variables for Host Access string string resbuff = new string(' ', 80); string reascode = "0000"; HABRSCI("0000", "01", "nucas.txt\0", resbuff, reascode); } catch (Exception ex) { string zzzz = ex.Message; } } I rewrote as a Service, everything works except the dll call, it just acts as it was never called at all. Here are some snips from my Service code.... //Set up usage of the .dll for Host Access [DllImport("C:\\Program Files\\OS_NUCAS_Reader\\libhab2.dll",EntryPoint = "HABRSCI")] public static extern int HABRSCI(string RSCI1, string RSCI2, string RSCI3, string RSCI4, string RSCI5); and later, inside of a timer event.. try { //Create Variables for Host Access string string resbuff = new string(' ', 80); string reascode = "0000"; HABRSCI("0000", "01", "nucas.txt\0", resbuff, reascode); } catch (Exception ex) { } This is just ignored!!! I know the code executes around it, I have even added lines to write to the Event log just before and after the call. If it works, a text file is created and a records is added to a log file maintained by the dll. none of these are happening so it apears that it ignores the call to the dll when running as Service. Any assistance would be much appreciated. Thanks, Don
Hi, I suggest you gather all information to find out what goes wrong, so: 1. make sure you create a log file, and append all significant progress/trace info to it, one line at a time. 2. don't ignore exceptions
catch (Exception ex) {string zzzz = ex.Message;}
andcatch (Exception ex) {}
don't make sense since you are not doing anything with the exception's information. Furthermore, you should always use Exception.ToString() keeping all the details. 3. Make sure your app has an overall try-catch on top of your regular exception processing, so even if you miss a local exception, you still catch it at the top level. You should apply this to your main thread (the static main method), as to every thread you create. 4. check and log all return valuespublic static extern int HABRSCI(...)
promises a result, so store it in an int and look at it. Maybe it is a status, with some negative value indicating a specific error. 5. don't trust defaults for P/Invoke; I don't care to remember what calling convention is used, I specify it explicitly on both sides (managed and unmanaged); example:[DllImport("Winmm.dll", CallingConvention=CallingConvention.StdCall)] static extern int sndPlaySound(IntPtr buffer, int dwFlags);
Hope this helps.Luc Pattyn [Forum Guidelines] [My Articles]
This month's tips: - before you ask a question here, search CodeProject, then Google; - the quality and detail of your question reflects on the effectiveness of the help you are likely to get; - use PRE tags to preserve formatting when showing multi-line code snippets.
-
Hi, I suggest you gather all information to find out what goes wrong, so: 1. make sure you create a log file, and append all significant progress/trace info to it, one line at a time. 2. don't ignore exceptions
catch (Exception ex) {string zzzz = ex.Message;}
andcatch (Exception ex) {}
don't make sense since you are not doing anything with the exception's information. Furthermore, you should always use Exception.ToString() keeping all the details. 3. Make sure your app has an overall try-catch on top of your regular exception processing, so even if you miss a local exception, you still catch it at the top level. You should apply this to your main thread (the static main method), as to every thread you create. 4. check and log all return valuespublic static extern int HABRSCI(...)
promises a result, so store it in an int and look at it. Maybe it is a status, with some negative value indicating a specific error. 5. don't trust defaults for P/Invoke; I don't care to remember what calling convention is used, I specify it explicitly on both sides (managed and unmanaged); example:[DllImport("Winmm.dll", CallingConvention=CallingConvention.StdCall)] static extern int sndPlaySound(IntPtr buffer, int dwFlags);
Hope this helps.Luc Pattyn [Forum Guidelines] [My Articles]
This month's tips: - before you ask a question here, search CodeProject, then Google; - the quality and detail of your question reflects on the effectiveness of the help you are likely to get; - use PRE tags to preserve formatting when showing multi-line code snippets.
Luc Good suggestions all. I may have edited out too much when posting the code... Anyway, I have run this with lines just before and after the HABRSCI call logging to an eventlog so I know it is getting to this line. As to exceptions, they never come into play, no errors are happening (I have logging events in those sections also, never happen) I can watch the app run by monitoring the lines being added to the event log, it loops every three minutes (current setting) and does several things, but the .dll never responds. I did test it by adding code to return an int, but that always stays at zero in the Service (= no result) but returns either 4,8, or 16 in the Console version (indicates type of response collected by .dll) If the .dll works, it generates a small text file with result data AND adds a 65 char line record to a text file in the local folder with status and timestamp information. This happens in the console version but not in the service version. I will try the explicit call in #5 above to see if that helps. Thanks, Don :)
-
Luc Good suggestions all. I may have edited out too much when posting the code... Anyway, I have run this with lines just before and after the HABRSCI call logging to an eventlog so I know it is getting to this line. As to exceptions, they never come into play, no errors are happening (I have logging events in those sections also, never happen) I can watch the app run by monitoring the lines being added to the event log, it loops every three minutes (current setting) and does several things, but the .dll never responds. I did test it by adding code to return an int, but that always stays at zero in the Service (= no result) but returns either 4,8, or 16 in the Console version (indicates type of response collected by .dll) If the .dll works, it generates a small text file with result data AND adds a 65 char line record to a text file in the local folder with status and timestamp information. This happens in the console version but not in the service version. I will try the explicit call in #5 above to see if that helps. Thanks, Don :)
Hi Don, some more ideas: 1. if you have the source of the unmanaged code, you can add logging there too. what I typically do is make it such that the unmanaged code logs to the managed world, using a delegate (which becomes a function pointer). 2. if the DLL has a simpler function somewhere, try that one first; or if you have the source, add a simple function and try it (e.g. something that takes two ints and returns the product). Success here proves you are accessing the DLL, and doing some P/Invoke correctly. 3. I was a little surprised by the explicit NULL char in one of the args; if you try to get a double NULL to the unmanaged world, I am not sure the marshaling will do that for you. 4. if the unmanaged code throws an exception, it might just kill the thread without you noticing (depending on which timer you use, you may get a separate thread). You said "as if the function was not called", does the code return well from the unmanaged function, i.e. does the next (log) line get executed? 5. If everything else fails my best guess is the unmanaged code executes but fails maybe because there is no user, hence no "My Documents" and the like. Good error trapping should catch that. :)
Luc Pattyn [Forum Guidelines] [My Articles]
This month's tips: - before you ask a question here, search CodeProject, then Google; - the quality and detail of your question reflects on the effectiveness of the help you are likely to get; - use PRE tags to preserve formatting when showing multi-line code snippets.
-
Hi Don, some more ideas: 1. if you have the source of the unmanaged code, you can add logging there too. what I typically do is make it such that the unmanaged code logs to the managed world, using a delegate (which becomes a function pointer). 2. if the DLL has a simpler function somewhere, try that one first; or if you have the source, add a simple function and try it (e.g. something that takes two ints and returns the product). Success here proves you are accessing the DLL, and doing some P/Invoke correctly. 3. I was a little surprised by the explicit NULL char in one of the args; if you try to get a double NULL to the unmanaged world, I am not sure the marshaling will do that for you. 4. if the unmanaged code throws an exception, it might just kill the thread without you noticing (depending on which timer you use, you may get a separate thread). You said "as if the function was not called", does the code return well from the unmanaged function, i.e. does the next (log) line get executed? 5. If everything else fails my best guess is the unmanaged code executes but fails maybe because there is no user, hence no "My Documents" and the like. Good error trapping should catch that. :)
Luc Pattyn [Forum Guidelines] [My Articles]
This month's tips: - before you ask a question here, search CodeProject, then Google; - the quality and detail of your question reflects on the effectiveness of the help you are likely to get; - use PRE tags to preserve formatting when showing multi-line code snippets.
Luc, Thanks for sparking an idea that did fix prt of the problem. Your #5 comment did the trick (the "My Documents" bit!) It appears that while it is running as a console app, any file calls are, unless otherwise directed, sent to the folder where the .exe file is. In my case, to make this old .dll work, there MUST be a .cfg configuration file AND a file name reference .txt file in the same folder as the libhab2.dll. As an console app, those were placed there by the installer. I guess I assumed that this was the case with a Service also. NOT.....! All calls are sent to the C:\WINNT\System32 folder. I placed copies of the .cfg and .txt (the .dll was alredy there) in there and it works! Now I just have to figure how to redirect the "local" folder of a system login. (I guess I could leave the files in system32 but that feels wrong somehow!) Error trapping did not help because the .dll just returned with a Zero and no other information. It had been writing to it's own log file, but I did not see it because I was expecting to to be created and added to in the C:\Program Files\OSNUCASReader folder, not C:\WINNT\System32. If I had done a global search of my HD looking for "hafelog.log", I would have saved a week of hairpulling! The log only had a line like this: "20080116115428 23KDGWDLOG F_Dialog e 00085370Internal processing error encountered during routine operations. " BUT, it would have let me know that the .dll was being called. I just was not thinking about the System32 folder (too many trees to see the forest I guess) Anyway, thanks for the suggestions and the spark to get me moving forward. Don :-D
-
Luc, Thanks for sparking an idea that did fix prt of the problem. Your #5 comment did the trick (the "My Documents" bit!) It appears that while it is running as a console app, any file calls are, unless otherwise directed, sent to the folder where the .exe file is. In my case, to make this old .dll work, there MUST be a .cfg configuration file AND a file name reference .txt file in the same folder as the libhab2.dll. As an console app, those were placed there by the installer. I guess I assumed that this was the case with a Service also. NOT.....! All calls are sent to the C:\WINNT\System32 folder. I placed copies of the .cfg and .txt (the .dll was alredy there) in there and it works! Now I just have to figure how to redirect the "local" folder of a system login. (I guess I could leave the files in system32 but that feels wrong somehow!) Error trapping did not help because the .dll just returned with a Zero and no other information. It had been writing to it's own log file, but I did not see it because I was expecting to to be created and added to in the C:\Program Files\OSNUCASReader folder, not C:\WINNT\System32. If I had done a global search of my HD looking for "hafelog.log", I would have saved a week of hairpulling! The log only had a line like this: "20080116115428 23KDGWDLOG F_Dialog e 00085370Internal processing error encountered during routine operations. " BUT, it would have let me know that the .dll was being called. I just was not thinking about the System32 folder (too many trees to see the forest I guess) Anyway, thanks for the suggestions and the spark to get me moving forward. Don :-D
Hi Don, glad to be able to help. yeah, I don't hesitate searching the whole C: drive for a file...; all too often something gets stored in strange places.
Member 4372837 wrote:
"20080116115428 23KDGWDLOG F_Dialog e 00085370Internal processing error encountered during routine operations. "
not the most useful message... :)
Luc Pattyn [Forum Guidelines] [My Articles]
This month's tips: - before you ask a question here, search CodeProject, then Google; - the quality and detail of your question reflects on the effectiveness of the help you are likely to get; - use PRE tags to preserve formatting when showing multi-line code snippets.