Solved

Unmanaged From C#

Posted on 2003-11-20
19
769 Views
Last Modified: 2007-12-19
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
0
Comment
Question by:derekthornton
  • 10
  • 9
19 Comments
 
LVL 10

Expert Comment

by:ptmcomp
Comment Utility
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
 
LVL 2

Author Comment

by:derekthornton
Comment Utility
Hrnm.. Nope, that didn't do it. It just errored again as usual.
0
 
LVL 10

Expert Comment

by:ptmcomp
Comment Utility
Getting the numer of bytes:

int numberOfBytes;

API.TOCRGetJobResults(myJobNr, ref numberOfBytes, null);
0
 
LVL 2

Author Comment

by:derekthornton
Comment Utility
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
 
LVL 10

Expert Comment

by:ptmcomp
Comment Utility
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
 
LVL 10

Expert Comment

by:ptmcomp
Comment Utility
Oops:

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

IntPtr is already a pointer
0
 
LVL 2

Author Comment

by:derekthornton
Comment Utility
I'm not quite understanding what the 'while loop' is for.
0
 
LVL 2

Author Comment

by:derekthornton
Comment Utility
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
 
LVL 2

Author Comment

by:derekthornton
Comment Utility
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
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 2

Author Comment

by:derekthornton
Comment Utility
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
 
LVL 10

Accepted Solution

by:
ptmcomp earned 500 total points
Comment Utility
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
 
LVL 10

Expert Comment

by:ptmcomp
Comment Utility
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
 
LVL 2

Author Comment

by:derekthornton
Comment Utility
Alright.But TOCRResultsHeader and TOCRResultsItem are classes, and SizeOf wants variables.
0
 
LVL 10

Expert Comment

by:ptmcomp
Comment Utility
Sorry:

tocRResultsHeaderSize= Marhal.SizeOf(typeof(TOCRResultsHeader));
tocRResultsItemSize = Marhal.SizeOf(typeof(TOCRResultsItem));
0
 
LVL 2

Author Comment

by:derekthornton
Comment Utility
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
 
LVL 2

Author Comment

by:derekthornton
Comment Utility
nevermind. I got it. THANK YOU SO MUCH!
0
 
LVL 10

Expert Comment

by:ptmcomp
Comment Utility
public MyStruct
{
    [MarshalAs(UnmanagedType.LPStr)] public string abc;
}

But where do you have texts? I only see float and int?
0
 
LVL 2

Author Comment

by:derekthornton
Comment Utility
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
 
LVL 10

Expert Comment

by:ptmcomp
Comment Utility
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

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Article by: Najam
Having new technologies does not mean they will completely replace old components.  Recently I had to create WCF that will be called by VB6 component.  Here I will describe what steps one should follow while doing so, please feel free to post any qu…
Entity Framework is a powerful tool to help you interact with the DataBase but still doesn't help much when we have a Stored Procedure that returns more than one resultset. The solution takes some of out-of-the-box thinking; read on!
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now