Unmanaged From C#

Still trying my OCR Program....

This Function from this .dll Library (for an OCR Project) does an OCR Job and then has a function I can call called "GetJobResults" which takes the JobNumber, an Integer (How many bytes to allocate for the Results) and a "results" Structure. The code looks as follows...

--------C++ Code---------------(Original Format)---------------
typedef struct tagTOCRRESULTSHEADER
{
    long            StructId;
      long            XPixelsPerInch;
      long            YPixelsPerInch;
      long            NumItems;
      float            MeanConfidence;
} TOCRRESULTSHEADER;

typedef struct tagTOCRRESULTSITEM
{
      unsigned short      StructId;
      unsigned short      OCRCha;
      float                  Confidence;
      unsigned short      XPos;
      unsigned short      YPos;
      unsigned short      XDim;
      unsigned short      YDim;
} TOCRRESULTSITEM;
typedef struct tagTOCRRESULTS
{
    TOCRRESULTSHEADER      Hdr;
    TOCRRESULTSITEM            Item[1];
} TOCRRESULTS;

EXTERN_C long WINAPI TOCRGetJobResults(long JobNo, long *ResultsInf, TOCRRESULTS *Results);
--------------------------------------------

I cannot get this to properly export to a DLL.( There are a lot of other functions too, this is the one I am ..'presently' stuck on ..)
I am rewriting this into C#.  However since C# does not allow pointers (and even the 'unsafe' blocks don't allow them very well either) it isn't wanting to work. I'm fully aware of the 'Ref' keyword, and it has proved utterly useless to me. The Function works as follows.

When a Job is Performed (using a different function that returns the JobNumber as an Integer), and then this TOCRGetJobResults function is invoked, the JobNo is passed through it. The ResultsInf is set to a number of bytes if, and ONLY if the third parameter is set to 0. If the parameter is 0, then ResultsInf is set to an Integer of the number of bytes needed to store the results of the OCR Process. This function then must be invoked a second time, passing the same JobNo, ResultsInf, but a TOCRResults OBJECT/Structure which THEN put the TOCRResults Object/structure into the memory area that is large enough to contain the results, and puts the results in Respectively.

The TOCRResults Structure calls upon two other object structures.
TOCRResultsHeader and TOCRResultsItem. TOCRResultsItem contains the actual Array of Character with the OCR results, and TOCRResultsHeader contains the number of Items in the array with some other data. All of this is performed in a Commercial .lib file that I cannot edit (trust me, I would if I could).

Because of the way the pointers are structured to so closely work with memory addresses, I just can't make C# talk to this library right. I have debugged MOST of this program, and this is pretty much the last step (getting the OCR Results after I've already done the OCR Job!!)
I have tried making the structures classes and structures in C#, neither changes very much except I encounter more problems with certain other aspects of the program, so I'm just going to find one that works and work around it.

here is my 'CURRENT' C# code. Please note, this Compiles and runs, but it does not work.
I will send the Entire .zip Solution to anyone who is willing to aid me and needs to see it.
---------------C# Code----------------------
[StructLayout(LayoutKind.Sequential)]
public struct TOCRResultsHeader
{
      public int            StructId;
      public int            XPixelsPerInch;
      public int            YPixelsPerInch;
      public int            NumItems;
      public float      MeanConfidence;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOCRResultsItem
{
      public int            StructId;
      public int            OCRCha;
      public float      Confidence;
      public ushort      XPos;
      public ushort      YPos;
      public ushort      XDim;
      public ushort      YDim;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOCRResults
{
      public TOCRResultsHeader      Hdr;
      public TOCRResultsItem[]      Item;
}
[StructLayout(LayoutKind.Sequential)]
public struct API
{
[DllImportAttribute("TOCRdll")]
      public static extern int TOCRGetJobResults(int JobNo, ref int ResultsInf,ref TOCRResults Results);
}

----------------

In the main body of the C++ File, the TOCRResults Object is declared as follows.

TOCRRESULTS  *TOCRResults = 0;

of course, this is impossible in C#, as pointing to a managed Structure isn't allowed in the language. So I must define TOCRResults as such...

TOCRResults TOCRResults = new TOCRResults();

Which is the proper way to initialize an object. However it is obvious this object is somehow meant to be set to 0 through use of a pointer, and I just can't do that in C#, even if I use "unsafe" blocks, it doesn't work.

I'm becomming very desperate here. It seems to me that C# is just incapable of what I am wanting to do, however I need to use it for this, I don't want a bunch of .dll's floating about in my program, especially unmanaged ones that are just hopping in and out of managed source code, it will make debugging in the future a massive chore.

If anyone can help me, I would REALLY appreciate it.
--------

Further Information :
Once GetJobResults is called for the first time, ResultsInf is used to Instantiate TOCRResults Object with enough memory to hold the Data. Since C# utilizes the garbage collector, this is un-needed, but the .lib commercial library still requires I do something with this. Again, I'll send the solution to anyone who will help. This is ..becoming very frustrating.

Derek Thornton
LVL 2
derekthorntonAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

ptmcompCommented:
StructLayout(LayoutKind.Sequential)]
public struct TOCRResultsHeader
{
    public int          StructId;
    public int          XPixelsPerInch;
    public int          YPixelsPerInch;
    public int          NumItems;
    public Single     MeanConfidence;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOCRResultsItem
{
    public Int16          StructId;
    public Int16          OCRCha;
    public Single     Confidence;
    public UInt16     XPos;
    public UInt16     YPos;
    public UInt16     XDim;
    public UInt16     YDim;
}
[StructLayout(LayoutKind.Sequential)]
public class TOCRResults
{
    public TOCRResultsHeader     Hdr;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=1000)]  // just allocate too much - don't know if that will work
    public TOCRResultsItem[]     Item;
}

public class API
{
[DllImportAttribute("TOCRdll")]
    public static extern int TOCRGetJobResults(int JobNo, ref int ResultsInf,TOCRResults Results);
}
0
derekthorntonAuthor Commented:
Hrnm.. Nope, that didn't do it. It just errored again as usual.
0
ptmcompCommented:
Getting the numer of bytes:

int numberOfBytes;

API.TOCRGetJobResults(myJobNr, ref numberOfBytes, null);
0
Learn SQL Server Core 2016

This course will introduce you to SQL Server Core 2016, as well as teach you about SSMS, data tools, installation, server configuration, using Management Studio, and writing and executing queries.

derekthorntonAuthor Commented:
That can't work. TOCRGetJobResults has to have an object of TOCRResults returned in the third parameter. The GetJobResults Function is not mine, it's in a Commercial Library I bought. I can't modify it.
0
ptmcompCommented:
public class API
{
[DllImportAttribute("TOCRdll")]
   public static extern int TOCRGetJobResults(int JobNo, ref int ResultsInf,ref IntPtr addr);
}

int numberOfBytes;
IntPtr addr;
IntPtr addrItem;

API.TOCRGetJobResults(myJobNr, ref numberOfBytes, IntPtr.Zero);
addr = Marhal.AllocHGlobal(numberOfBytes);
API.TOCRGetJobResults(myJobNr, ref numberOfBytes, addr);
TOCRResultsHeader hdr = (TOCRResultsHeader)Marshal.PtrToStructure(addr,  typeof(TOCRResultsHeader));
while ....
{
    addrItem = (IntPtr)((int)addr + Marhal.SizeOf(TOCRResultsItem));
    TOCRResultsItem item = (TOCRResultsItem)Marshal.PtrToStructure(addrItem,  typeof(TOCRResultsItem));
}
0
ptmcompCommented:
Oops:

 public class API
{
[DllImportAttribute("TOCRdll")]
  public static extern int TOCRGetJobResults(int JobNo, ref int ResultsInf,IntPtr addr);
}

IntPtr is already a pointer
0
derekthorntonAuthor Commented:
I'm not quite understanding what the 'while loop' is for.
0
derekthorntonAuthor Commented:
Alright.I'm even more confused now, I haven't any idea what you just did. I can't make any sense of it.
0
derekthorntonAuthor Commented:
Alright. Hang on, I think I'm starting to get this...

So we create two pointers on an unmanaged memory heap and use them instead of managed variables? I'm still confused as to what the While loop needs to be testing for.
0
derekthorntonAuthor Commented:
Alright! The "Header" part works. Now I just need to work on the Items Parts. Sorry if I'm being annoying, I'm just learning this Marshalling stuff, it's rather new to me, and the code REALLY confused me until I sat down and picked it apart for a few moments. So, after the Header part, before the "While ....." and below is what I'm lost on.
0
ptmcompCommented:
nt numberOfBytes;
IntPtr addr;
IntPtr tocRResultsItemBaseAddr;
IntPtr addrItem;
int tocRResultsHeaderSize;
int tocRResultsItemSize;
int numItems;

tocRResultsHeaderSize= Marhal.SizeOf(TOCRResultsHeader);
tocRResultsItemSize = Marhal.SizeOf(TOCRResultsItem);

API.TOCRGetJobResults(myJobNr, ref numberOfBytes, IntPtr.Zero);
// reserve the needed amount of bytes for the result
addr = Marhal.AllocHGlobal(numberOfBytes);

// let the function fill the memory with the data
API.TOCRGetJobResults(myJobNr, ref numberOfBytes, addr);
// get the header back in managed memory
TOCRResultsHeader hdr = (TOCRResultsHeader)Marshal.PtrToStructure(addr,  typeof(TOCRResultsHeader));

numItems = (numberOfBytes - tocRResultsHeaderSize) / tocRResultsItemSize;
// address of the first item
tocRResultsItemBaseAddr = (IntPtr)((int)addr + tocRResultsHeaderSize);

for(int i=0; i<numItems; i++)
{
    // calculate the base address of a item (the first = tocRResultsItemBaseAddr, 2nd = tocRResultsItemBaseAddr + size of item, ...)
    addrItem = (IntPtr)((int)tocRResultsItemBaseAddr + i * tocRResultsItemSize);
    TOCRResultsItem item = (TOCRResultsItem)Marshal.PtrToStructure(addrItem,  typeof(TOCRResultsItem));
}
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ptmcompCommented:
Marhal.FreeHGlobal(addr);  // since all is copied to managed memory now you can free the unmanaged memory (place try/finally to make sure that the memory is deallocated)
0
derekthorntonAuthor Commented:
Alright.But TOCRResultsHeader and TOCRResultsItem are classes, and SizeOf wants variables.
0
ptmcompCommented:
Sorry:

tocRResultsHeaderSize= Marhal.SizeOf(typeof(TOCRResultsHeader));
tocRResultsItemSize = Marhal.SizeOf(typeof(TOCRResultsItem));
0
derekthorntonAuthor Commented:
Alright.Almost there I think.

It stores them as ints, meaning it's all numbers, and I'm assuming that they are in Ascii or Unicode format. Any clues as to what I should do to do convert them to ascii/unicode text?
0
derekthorntonAuthor Commented:
nevermind. I got it. THANK YOU SO MUCH!
0
ptmcompCommented:
public MyStruct
{
    [MarshalAs(UnmanagedType.LPStr)] public string abc;
}

But where do you have texts? I only see float and int?
0
derekthorntonAuthor Commented:
This small program is part of a bigger picture. I am trying to isolate everything so that it will be contained within a function/method and all I do is pass it the FileName, and it returns a string value of the results. (Which I've successfully done at the time of this typing).

This .dll (C#.Dll) will load into a Form Application (Along with a few other things I've written myself) and be a much more versatile OCR Application. I need it like this because I plan to have selection areas that can OCR'ed Individually. Thanks again for your help!

To convert it, I merely typecasted with a char, btw.
0
ptmcompCommented:
System.Text.Encoding encoding = System.Text.Encoding.Default; // default ANSI Code Page
  byte[] buffer = new byte[30];
  [...] // fill buffer with ASCII / ANSI string
  string result = encoding.GetString(buffer);
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C#

From novice to tech pro — start learning today.