CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 7 of 7
  1. #1
    Join Date
    Sep 2009
    Location
    North Carolina
    Posts
    52

    SEHException in P/Invoke

    Have a C# class which wraps some C++ functions in a dll. Code below shows the setup for the call to the first native method.
    <code>
    using System;
    using System.Configuration;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;

    namespace SumProgram
    {
    class SumApp
    {
    IntPtr rdir;
    IntPtr lic;
    IntPtr key;
    IntPtr genre;
    IntPtr variant;
    IntPtr lang;
    IntPtr encoding;
    IntPtr format;
    uint num_sent;
    bool offset_only;
    bool norm_score;
    uint num_phr;
    int maxChars;
    IntPtr summary;

    public SumApp()
    {
    String sRdir = ConfigurationManager.AppSettings["rdir"];
    rdir = Marshal.StringToHGlobalAnsi(sRdir);
    Console.WriteLine("rdir " + Marshal.PtrToStringAnsi(rdir));
    String sLic = ConfigurationManager.AppSettings["lic"];
    lic = Marshal.StringToHGlobalAnsi(sLic);
    Console.WriteLine("license " + Marshal.PtrToStringAnsi(lic));
    String sKey = ConfigurationManager.AppSettings["key"];
    key = Marshal.StringToHGlobalAnsi(sKey);
    Console.WriteLine("key " + Marshal.PtrToStringAnsi(key));
    createSummarizer(rdir, lic, key);
    }

    [DllImportAttribute("csummarizer.dll", EntryPoint = "createSummarizer")]
    public static extern void createSummarizer(IntPtr rdir, IntPtr lic, IntPtr key);
    }
    }
    </code>

    The output from the Console.WriteLines is:

    rdir lang
    license lang\license.dat
    key <key>

    the function in the documentation is as follows:
    summarizer summarizer("resource_dir", "license_file", "key");

    At the point of the call to createSummarizer I get
    "Runtime Error!
    This application has requested the Runtime to terminated in an unusual way"

    In debug mode the error dump is:
    'SumProgram.vshost.exe' (Managed): Loaded 'C:\WINDOWS\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c561934e089\System.Xml.dll'
    The thread 0xc6c has exited with code 0 (0x0).
    'SumProgram.vshost.exe' (Managed): Loaded 'D:\C#Service\SumProgram\SumProgram\bin\Debug\SumProgram.exe', Symbols loaded.
    A first chance exception of type 'System.Runtime.InteropServices.SEHException' occurred in SumProgram.exe
    The program '[3312] SumProgram.vshost.exe: Managed' has exited with code 3 (0x3).

    any idea how I can track this done to a fixable problem?
    Jim

  2. #2
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: SEHException in P/Invoke

    I would try:

    1) change all the pinvoke IntPtr and manual marshaling to use string declarations as suggested in the previous post.
    2) Add the SetLastError pinvoke attribute and put the C# method call in a try/catch block to see if you can catch additional information.
    3) Create a C++ test project and see if you have the same issue. If it works here, you have a marshaling problem.

  3. #3
    Join Date
    Sep 2009
    Location
    North Carolina
    Posts
    52

    Re: SEHException in P/Invoke

    Ok changed it all around so it looks like this:
    <code>
    using System;
    using System.Configuration;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;

    namespace SumProgram
    {
    class SumApp
    {
    String rdir;
    String lic;
    String key;
    String genre;
    String variant;
    String lang;
    String encoding;
    String format;
    uint num_sent;
    bool offset_only;
    bool norm_score;
    uint num_phr;
    int maxChars;
    StringBuilder summary;

    public SumApp()
    {
    rdir = ConfigurationManager.AppSettings["rdir"];
    Console.WriteLine("rdir " + rdir);
    lic = ConfigurationManager.AppSettings["lic"];
    Console.WriteLine("license " + lic);
    key = ConfigurationManager.AppSettings["key"];
    Console.WriteLine("key " + key);
    genre = ConfigurationManager.AppSettings["genre"];
    Console.WriteLine("genre " + genre);
    variant = ConfigurationManager.AppSettings["variant"];
    Console.WriteLine("variant " + variant);
    lang = ConfigurationManager.AppSettings["lang"];
    Console.WriteLine("lang " + lang);
    encoding = ConfigurationManager.AppSettings["encoding"];
    Console.WriteLine("encoding " + encoding);
    format = ConfigurationManager.AppSettings["format"];
    Console.WriteLine("format " + format);
    num_sent = Convert.ToUInt32(ConfigurationManager.AppSettings["num_sent"]);
    Console.WriteLine("num_sent " + num_sent);
    String sOffset = ConfigurationManager.AppSettings["offset_only"];
    offset_only = (sOffset == "1" ? true : false);
    Console.WriteLine("offset_only " + offset_only);
    String sNorm = ConfigurationManager.AppSettings["norm_score"];
    norm_score = (sNorm == "1" ? true : false);
    Console.WriteLine("norm_score " + norm_score);
    num_phr = Convert.ToUInt32(ConfigurationManager.AppSettings["num_phr"]);
    maxChars = Convert.ToInt32(ConfigurationManager.AppSettings["maxChars"]);
    Console.WriteLine("maxChars " + maxChars);
    Console.WriteLine("num_phr " + num_phr);
    try
    {
    createSummarizer(rdir, lic, key);
    createInputOptions(genre, variant, lang, encoding, format);
    setSentenceOutput(num_sent, offset_only, norm_score);
    setPhraseOutput(num_phr, offset_only);
    summary = new StringBuilder(maxChars);
    }
    catch(SEHException e)
    {
    Console.WriteLine(e.Message);
    }
    }

    [DllImportAttribute("csummarizer.dll", EntryPoint = "createSummarizer", SetLastError = true)]
    public static extern void createSummarizer(String rdir, String lic, String key);

    [DllImportAttribute("csummarizer.dll", EntryPoint = "createInputOptions")]
    public static extern void createInputOptions(String genre, String variant, String lang, String encoding, String format);

    [DllImportAttribute("csummarizer.dll", EntryPoint = "setSentenceOutput")]
    public static extern void setSentenceOutput(uint num_sent, bool offset_only, bool norm_score);

    [DllImportAttribute("csummarizer.dll", EntryPoint = "setPhraseOutput")]
    public static extern void setPhraseOutput(uint num_phr, bool offset_only);

    [DllImportAttribute("csummarizer.dll", EntryPoint = "getSummary")]
    public static extern StringBuilder getSummary(String filename);

    public StringBuilder produceSummary(String filePath)
    {
    summary = getSummary(filePath);
    return(summary);
    }
    }
    }
    </code>
    Also put in LastError got this in the console:

    Unhandled Exception: System.AccessViolationException: Attempted to read or write
    protected memory. This is often an indication that other memory is corrupt.
    at SumProgram.SumApp.getSummary(String filename)
    at SumProgram.SumApp.produceSummary(String filePath) in D:\C#Service\SumProgr
    am\SumProgram\SumApp.cs:line 88
    at SumProgram.Sum.Summarizer.Main(String[] args) in D:\C#Service\SumProgram\S
    umProgram\Summarizer.cs:line 19

    In debug the state of the variable at exception time was:

    bstr {...} inxight::byte_stream_interface &
    input_options {s_doc_genre=??? s_variant=??? s_language=??? ...} const inxight::summarization_input_options &
    phrase_output_options {s_no_phrases=??? s_use_phrase_percentage=??? s_number_phrases=??? ...} const inxight::summarization_phrase_output &
    query_encoding 0x00000000 <Bad Ptr> const char *
    query_length 0 unsigned int
    query_string 0x00000000 <Bad Ptr> const char * s {...}
    inxight::summarizer_interface &
    s_implementation 0x545c376e inxight::summarization_interface *
    sentence_output_options {s_no_sentences=??? s_use_sentence_percentage=??? s_number_sentences=??? ...} const inxight::summarization_sentence_output &
    this 0x010428d8 {s_implementation=0x545c376e } inxight::summarization * const

    this is from deep in the vendor's dll.

    Thanks
    Jim

  4. #4
    Join Date
    May 2007
    Posts
    1,546

    Re: SEHException in P/Invoke

    Firstly what function is causing the error? In the first post you say 'createSummarizer' causes the issue but in this case it's 'produceSummary'. Which is it? Or is it both? Secondly, can you paste the declarations of the native functions you're trying to P/Invoke?

    Code:
    public StringBuilder produceSummary(String filePath)
    A StringBuilder as a return type for a native function looks very suspicious. If the function is returning a char*, then marshal that as a string. If it's returning a char **, marshal that as an IntPtr and then manually get the string from that. I can't think of any case where StringBuilder would be the correct option as a return type.

    Memory management might also be important here. Does the vendor library duplicate strings which you pass to its methods? Does it take ownership of the string supplied to it? i.e. is it responsible for storing it and free'ing it later. If it's responsible for free'ing it, you have to ensure that you allocate the memory for the string using the right methods.

    Also, if it returns a string to you (char*), are you responsible for freeing it? If so what method should you use to free it. Note that if you marshal a native char* as a .NET string, the .NET framework will automatically free the pointer. If you *do not* want this to happen you should marshal the char* as an IntPtr and use Marshal.PtrToAnsiString (or whatever) to get your string value.

    As always, this link probably has more than you ever want to know about marshalling strings: http://mono-project.com/Interop_with_Native_Libraries
    www.monotorrent.com For all your .NET bittorrent needs

    NOTE: My code snippets are just snippets. They demonstrate an idea which can be adapted by you to solve your problem. They are not 100% complete and fully functional solutions equipped with error handling.

  5. #5
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: SEHException in P/Invoke

    The following looks suspect (btw, please use code tags [ code] [/ CODE]):

    Code:
     
    [DllImportAttribute("csummarizer.dll", EntryPoint = "getSummary")]
    public static extern StringBuilder getSummary(String filename);
    The reason is that functions shouldn't return pointers to strings. So if this is working in C++, you're lucky.

    To illustrate, look at any of the Windows api's that need to 'return' string data. Rather than returning the string data, they all require string buffer param, and a size (of buffer) param. The function 'returns' the string data by filling in the buffer.

    For example, here's the declaration for GetModuleFileName( ) api which returns the full path name of the running process (among other things).

    Code:
    DWORD WINAPI GetModuleFileName(
      __in_opt  HMODULE hModule,
      __out     LPTSTR lpFilename,
      __in      DWORD nSize
    );
    The PInvoke signature (from www.pinvoke.net) is:

    Code:
    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern uint GetModuleFileName
    (
        IntPtr hModule,
        StringBuilder lpFilename, 
        [MarshalAs(UnmanagedType.U4)]
        int nSize
    );
    In C++, we pass in a buffer to the function.

    Code:
     
    TCHAR szBuffer[ MAX_PATH ] = { 0 };
    GetModuleFileName( NULL, szBuffer, MAX_PATH );
    In C#, we allocate the StringBuilder (which you are doing):

    Code:
     
    StringBuilder sb = new StringBuilder( 260 );
    GetModuleFileName( IntPtr.Zero, sb, sb.Capacity );
    Console.WriteLine( sb.ToString( ) );
    You can also wrap the pinvoke with a helper method:

    Code:
     
    public string GetModuleFileName( )
    {
      StringBuilder sb = new StringBuilder( 260 );
      GetModuleFileName( IntPtr.Zero, sb, sb.Capacity );
      return sb.ToString( );
    }

  6. #6
    Join Date
    Sep 2009
    Location
    North Carolina
    Posts
    52

    Re: SEHException in P/Invoke

    First, to Mutant_Fruit, had problems with createSummarizer when using the code in my first posting, then I followed Arjay's advice and changed to the code in my second posting, now I have a problem with produce summary.
    The signatures from the dll header
    Code:
    #define SUMMARIZER_EXPORT __declspec(dllexport)
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    
    SUMMARIZER_EXPORT void createSummarizer(const char *rdir, const char *license, const char *key);
    SUMMARIZER_EXPORT void setInOptions(const char *doc_genre, const char *variant, const char *language, const char *encoding, const char *doc_format);
    SUMMARIZER_EXPORT void setSentOptions(unsigned int sentences_wanted, bool sentence_offset, bool normalize_score);
    SUMMARIZER_EXPORT void setPhraseOptions(unsigned int phrases_wanted, bool phrase_offset_only);
    SUMMARIZER_EXPORT string* getSummary(char *filename);
    
    #ifdef __cplusplus
    }
    #endif
    SUMMARIZER_EXPORT is defined as __declspec(dllexport)
    All function are with a @ifdef __cplusplus block.
    Will check your citation

    Now Arjay
    Have several articles which say that if you have an in/out or out parameter of character data the StringBuilder is the class to use. Now here I'm using it as a return. As you can see above, it is a string as defined in C++. So how do I handle that?

    I had to use string because of the way I had to patse their results as below:
    Code:
    string* getSummary(const char *filename)
    {
    	file_byte_stream *fbs = new file_byte_stream(filename);
    	summarization summary (*summarzr, *fbs, *inoptions, *sentOptions, *phraseOptions);
    	resizable_string<char> outStr("");
    	outStr += "Sentences\n";
    	for (sequence<key_item>::const_iterator siter = summary.first_key_sentence();siter != summary.end_key_sentence(); siter++)
    	{
    		inxight::text line = siter->item_text();
    		for(unsigned int i=0; i<line.length(); i++)
    		{
    			outStr += (char)line[i].code();
    		}
    		outStr += "\n";
    	}
    	for (sequence<key_item>::const_iterator iter = summary.first_key_phrase(); iter != summary.end_key_phrase(); iter++)
    	{
    		inxight::text line = iter->item_text();
    		for(unsigned int i=0; i<line.length(); i++)
    		{
    			outStr += (char)line[i].code();
    		}
    		outStr += "\n";
    	}
    	outStr += "\0";
    	//cout << outStr.ptr();
    	cout << outStr.size();
    	string *o = new string(outStr.ptr());
    	char *out = new char[outStr.size()];
    	out = outStr.ptr();
    	return(o);
    }
    In short, I needed a growable container.

    Thanks,
    Jim

  7. #7
    Join Date
    May 2007
    Posts
    1,546

    Re: SEHException in P/Invoke

    The C# declaration for this is either:

    public string getSummary(string filename);

    or

    public IntPtr getSummary (IntPtr filename)

    Try the first option first. If it does not work, you should use the second declaration and manually convert the .NET string to an IntPtr using the conversion methods in the Marshal class. [0]

    You don't need a growable container because the method is just returning a plain old char* which it allocates itself. The only time you need a growable container (a stringbuilder) is when you supply a buffer to the method which is then filled in.

    [0] Just to add - the reason for using the second method is that the library *might* use custom functions for allocating and freeing memory. If you use the standard .NET mechanism to allocate memory for a string and then pass it to the library, it's possible that the library will then explode randomly later on when it attempts to free that string. Thats why you may have to represent the char* as an IntPtr as opposed to a string. Hope that makes sense. My previous link explains all this much better than this brief description
    Last edited by Mutant_Fruit; October 28th, 2009 at 07:21 PM.
    www.monotorrent.com For all your .NET bittorrent needs

    NOTE: My code snippets are just snippets. They demonstrate an idea which can be adapted by you to solve your problem. They are not 100% complete and fully functional solutions equipped with error handling.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured