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

    Getting errors from PInvoke

    Have used PInvoke to "wrap" 3 function from a dll. The dll did not have a manifest and there were a lot of #define for datatypes, so I had to work back through them to find all the basic datatypes. I used these when I put the functions in the snippets window of PInvoke assistant and generated signatures to use in C#.
    With a little tweaking, I got it to compile and run. However, the output I'm getting is either not there or not what I expect. e.g have a ushort which should return a value found in the sccerr.h file which are in the range 0-100, I'm getting something over 33000.
    Is the a way to get errors or warning from the dll or lib?
    Jim

  2. #2
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940

    Re: Getting errors from PInvoke

    It sounds like your PInvoke signatures are incorrect.

    Is the a way to get errors or warning from the dll or lib
    It depends on how these warnings/errors are exposed by the dll.

    If they're thrown as C++ exceptions no - .NET has no concept of a C++ exception. To process C++ exceptions you can either

    (1) Write an 'interface' native dll which makes the calls into your dll with native C++ try..catch blocks for the known exceptions. The C# app would then make calls to this dll, not the final dll. You can then pass the error messages back to C# through PInvoke using whichever method you think is appropriate.
    (2) Write the .NET interface to the dll in C++/CLI.

    If you post

    (1) Your PInvoke method signatures
    (2) The C++ method signatures (from the .h file)

    then I'll be able to tell you where you're going wrong.

    Alternatively try using one of the PInvoke tools which are on the net - have a look at the wikipedia page for PInvoke.

    Darwen.
    www.pinvoker.com - PInvoker - the .NET PInvoke Interface Exporter for C++ Dlls.

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

    Re: Getting errors from PInvoke

    Sorry for the delay.
    Here's the info
    Functions defined in header file:

    FI_ENTRYSC VTWORD FI_ENTRYMOD FIIdFileEx(VTDWORD, VTVOID *, VTDWORD, VTWORD *,VTLPTSTR, VTWORD);
    FI_ENTRYSC VTDWORD FI_ENTRYMOD FIInit(VTVOID);
    FI_ENTRYSC VTDWORD FI_ENTRYMOD FIDeInit(VTVOID);

    where FI_ENTRYSC is #define __declspec(dllexport) if WIN32 is defined
    FI_ENTRYMOD is #define __cdecl if WIN32 is defined

    VTWORD is typedef unsigned short
    VTDWORD is typedef unsigned long
    VTVOID is typedef void
    VTLPTSTR is typedef VTTCHAR *
    VTTCHAR is typedef VTCHAR
    VTCHAR is typedef char

    I plug these into PInvoke Assistant I get:

    [System.Runtime.InteropServices.DllImportAttribute("sccfi.dll", EntryPoint = "FIInit")]
    public static extern uint FIInit();

    [System.Runtime.InteropServices.DllImportAttribute("sccfi.dll", EntryPoint = "FIIdFileEx")]
    public static extern ushort FIIdFileEx(uint pathType, System.IntPtr path, uint flag, ref ushort fileType, System.IntPtr typeName, ushort maxChars);

    [System.Runtime.InteropServices.DllImportAttribute("sccfi.dll", EntryPoint = "FIDeInit")]
    public static extern uint FIDeInit();

    the return I get from FIInit is 2304
    the return I get from FIIdFileEx is always 65535, if the 5th parameter got loaded with a char value, the return should be 0 which is "OK"

    Thanks,
    Jim

  4. #4
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940

    Re: Getting errors from PInvoke

    65535 is -1 when held by a short.

    This suggests an error code.

    if the 5th parameter got loaded with a char value
    The 5th parameter is a char * - what code are you using to produce the IntPtr to pass into this parameter ?

    Darwen.
    www.pinvoker.com - PInvoker - the .NET PInvoke Interface Exporter for C++ Dlls.

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

    Re: Getting errors from PInvoke

    Ok the value in the short is good news if -1 is not the default value, at least there is a return.
    The 5th parameter is an empty one passed in to be loaded, am setting it up like this:
    System.IntPtr typeName; (declaration)
    typeName = IntPtr.Zero;
    typeName = Marshal.AllocateHGlobal(IntPtr.Size);

    The 2d parameter I setup like this:
    System.IntPtr path = Marshal.StringToGlobalUni(row.Doc_path);
    where row.Doc_path contains a file path as a String.

  6. #6
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940

    Re: Getting errors from PInvoke

    The 2nd parameter should be :

    Code:
    System.IntPtr path = Marshal.StringToHGlobalAnsi(row.Doc_path);
    ToHGlobalUni will create a unicode string - not what the function is expecting.

    Also you should allocate the size of memory you're specifying as the last parameter e.g.

    Code:
    ushort typeNameSize = 100;
    IntPtr typeName = Marshal.AllocateHGlobal((int)typeNameSize);
    FIIdFileEx(....., typeName, typeNameSize);
    Don't forget to call Marshal.FreeHGlobal on both these pointers after calling the method otherwise you'll end up with a memory leak.

    Darwen.
    Last edited by darwen; October 21st, 2009 at 02:36 PM.
    www.pinvoker.com - PInvoker - the .NET PInvoke Interface Exporter for C++ Dlls.

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

    Re: Getting errors from PInvoke

    Made the changes you pointed out. I get the same results, so can only assume I'm still doing something wrong. There are a couple other parameters I'm iffy about. Let me insert the C# class involved.
    Code:
    using System;
    using System.Runtime.InteropServices;
    using System.Configuration;
    
    
    namespace FIProgram
    {
        class FileIDApp
        {
            ushort err;
            uint flag;
            uint pathType;
            ushort maxChars;
            ushort fileType;
            System.IntPtr typeName;
    
    
            public FileIDApp()
            {   
                maxChars = 4096;
                typeName = Marshal.AllocHGlobal((int)maxChars);
                String sFlag = ConfigurationManager.AppSettings["flag"];
                String sPathType = ConfigurationManager.AppSettings["pathType"];
                if(flag.Equals("normal")){this.flag = 0;}
                else if(flag.Equals("extended")){this.flag = 1;}
                if(pathType.Equals("ansi")){this.pathType = 2;}
                else if(pathType.Equals("unicode")){this.pathType = 4;}
                uint rtn = FIInit();
                Console.WriteLine("Return from FIInit " + rtn);
            }
    
            [System.Runtime.InteropServices.DllImportAttribute("sccfi.dll", EntryPoint = "FIInit")]
            public static extern uint FIInit();
    
            [System.Runtime.InteropServices.DllImportAttribute("sccfi.dll", EntryPoint = "FIIdFileEx")]
            public static extern ushort FIIdFileEx(uint pathType, System.IntPtr path, uint flag, ref ushort fileType, System.IntPtr typeName, ushort maxChars);
    
            [System.Runtime.InteropServices.DllImportAttribute("sccfi.dll", EntryPoint = "FIDeInit")]
            public static extern uint FIDeInit();
    
            public String processDocuments(System.IntPtr path)
    
            {
                Console.WriteLine("Path " + Marshal.PtrToStringAnsi(path));
                err = FIIdFileEx(pathType, path, flag, ref fileType, typeName, maxChars);
                Console.WriteLine("Error " + err);
                if (err == 0)
                {
                    Logger.addToLog("OK");
                    return (Marshal.PtrToStringAnsi(typeName));
                }
                else
                {
                    return (setError());
                }
            }
    
            public String setError()
            {
                String result = "";
                if (err == 14)
                {
                    result = "Unsupported Format";
                }
                else
                {
                    result = "Error Code " + err;
                }
                return (result);
            }
        }
    }
    Ok, the parameters I mean are flag, pathType and err.
    First flag and pathType, values for flag are defined in one of the header files as:
    Code:
        #define FIFLAG_NORMAL                    0
        #define FIFLAG_EXTENDEDFI             1
        #define FIFLAG_OVERRIDEOPTIONS   2
    There is a whole header file to define values for pathType, so I'm using only two (IOTYPE_ANSIPATH and ISOTYPE_UNICODEPATH)
    Since I know that C# may not know about those #defines, you can see in the constructor I'm pulling in a string from the app.config file and using it to assign the appropriate number to flag or pathType.
    I just put a Console.WriteLine after the assignment block for these two and got 0 from both. Got rid of the "this" in the assignments and it made no difference.

    Now err, if 65535 is -1 in a short then my "if(err == 0)" is total nonsense. How do I get a value like -1 or zero out of and short?

    Thanks much for help
    Jim

    How do you get the code tag to work on here
    Last edited by Arjay; October 22nd, 2009 at 01:45 PM. Reason: Fixed up code tags to use brackets

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

    Re: Getting errors from PInvoke

    You seem to be doing some extra conversion code that I don't believe is required.

    I'm not sure why you use IntPtr for string parameters.

    Try just declaring them as strings and set the appropriate CharSet attribute.

    For example, let's look at the PInvoke signature for the FindWindow api:

    Code:
    [DllImport( "user32.dll", SetLastError = true, CharSet = CharSet.Auto )]
    publicstaticexternIntPtr FindWindow( string lpClassName, string lpWindowName );


    Is there any reason you can't declare the strings in your api similarly?

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

    Re: Getting errors from PInvoke

    You should carefully read the first post above and note a few things. First the signatures I'm using were generated by PIinvoke Assistant from the signatures declared in the header file. They are not the product of some random determination on my part. the use of the pointers is required. In the case of the path parameter the C++ function is looking for an address to a block of memory which contains the path data. It is not looking for a CRL string for which the address is hidden and therefore not accessible to C++. The second pointer typeName the C++ function is looking for an address to an empty block of memory into which it can write part of it's response.
    The marshalling of parameters is not a trivial matter.

    Jim

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

    Re: Getting errors from PInvoke

    Quote Originally Posted by jjones7947 View Post
    You should carefully read the first post above and note a few things. First the signatures I'm using were generated by PIinvoke Assistant from the signatures declared in the header file. They are not the product of some random determination on my part. the use of the pointers is required. In the case of the path parameter the C++ function is looking for an address to a block of memory which contains the path data. It is not looking for a CRL string for which the address is hidden and therefore not accessible to C++. The second pointer typeName the C++ function is looking for an address to an empty block of memory into which it can write part of it's response.
    The marshalling of parameters is not a trivial matter.

    Jim
    I did carefully read your first post - you made no mention of generating the signatures using "PInvoke Assistant". "PInvoke", was mentioned, but that was it. If you are passing in strings like it looks like param 5 is, then why not prototype at least this param as a string?

    At any rate, keep in mind that my comments are an attempt at helping you solve your problem.

  11. #11
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940

    Re: Getting errors from PInvoke

    Arjay is right, you really should be using the standard marshalling for strings in the PInvoke. However, let's continue in the vein that we've been running in.

    PInvoke interop assistant takes the only option it has based on the information - a const char * parameter could potentially be any of the following depending on context : byte[], string, StringBuilder, IntPtr.

    Only IntPtr satisfies all criteria.

    Now to deal with the problem :

    Can I suggest you just stick with ansi for the file path type - otherwise you'll have to change your marshalling code from StringToHGlobalAnsi to StringToHGlobalUnicode depending on what you've passed in.

    You should be specifying the path type as ANSI if you're using StringToHGlobalAnsi for instance.

    I think you're problems are starting to lie with the parameters you're passing into the function rather than the marshalling now - you'll have to check the documentation of the function to make sure you're passing the correct values.

    Unfortunately what you should be passing in isn't something I can help with - it'll depend on what the function is expecting which you can only get from the documentation.

    Darwen.

    P.S. Arjay, he did say that he used Pinvoke interop assistant :

    I plug these into PInvoke Assistant I get:
    Last edited by darwen; October 23rd, 2009 at 05:09 AM.
    www.pinvoker.com - PInvoker - the .NET PInvoke Interface Exporter for C++ Dlls.

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

    Re: Getting errors from PInvoke

    In the case of the path parameter the C++ function is looking for an address to a block of memory which contains the path data.
    Which means you can marshal the path as a string in C#. It will automagically be converted to a standard char *. It will convert using the default encoding of the platform unless you specifically request otherwise in the P/Invoke declaration.

    The second pointer typeName the C++ function is looking for an address to an empty block of memory into which it can write part of it's response.
    StringBuilder can be used for this. Preallocate the required space in your stringbuilder and change the P/Invoke signature to take a StringBuilder instead of an IntPtr.

    http://mono-project.com/Interop_with...fiable_Strings

    That page explains all you'd ever want to know about the magic that goes on behind the scenes when interop'ing with native libraries using strings.
    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.

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

    Re: Getting errors from PInvoke

    Well, there may be various ways of marshalling non-primitives across the language barrier, but this one does work. It turns out that the assignment routines for "flag" and "pathType" were not done correctly. I pulled in the strings from the app.cfg file but did not use them in the conditional statements. Instead I used the uint variables, which made the conditions all "false" so nothing got assigned. Fixing this put values in those parameters and now it works fine.
    Thanks for the help. This was so informative, I think I'll try it on another dll I have.

    Jim

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

    Re: Getting errors from PInvoke

    It's better to use string/stringbuilder when possible because then you won't leak memory. Doing it manually has no benefits other than increasing the possibility of a memory leak, one the GC can't help with.

    Glad it's working now anyway.
    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.

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

    Re: Getting errors from PInvoke

    Can you give an example of how I can do either? Can I just pass in the string I already have?
    Course I'd need to change the function signature?
    Jim

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