Type Marshaling when using a C++ dll in C#

Started by
5 comments, last by JamesLewis 15 years, 12 months ago
Hello. I need some help with type marshaling in C#. I'm trying to use the ESCAPI 2.0 dll, which was written in C++, in a C# class. The dll is available at http://sol.gfxile.net/zip/escapi21.zip. There are example escapi.h and escapi.cpp files there for using this dll from C++. I've tried to convert this into C#, however I don't know how to setup a pointer to a C# struct. In the example files the struct is called SimpleCapParams and in my code it's named CapParams. The code I have right now in C# compiles and runs but it doesn't actually capture a picture because the mTargetBuf array is empty and I'm sure this is because I haven't set it up right with Marshaling (and because the initCapture method returns a one for success). Can anyone tell me what I'm doing wrong? Here's my C# code so far: using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; namespace RobotServer { class CameraManager { [StructLayout(LayoutKind.Sequential)] struct CapParams { [MarshalAs(UnmanagedType.SafeArray)] public int[] mTargetBuf; public int mHeight; public int mWidth; } [DllImport("escapi.dll")] private static extern int countCaptureDevices(); [DllImport("escapi.dll")] private static extern int initCapture(uint deviceno, CapParams capParams); [DllImport("escapi.dll")] private static extern void deinitCapture(uint deviceno); [DllImport("escapi.dll")] private static extern void doCapture(uint deviceno); [DllImport("escapi.dll")] private static extern int isCaptureDone(uint deviceno); [DllImport("escapi.dll")] private static extern void getCaptureDeviceName(uint deviceno, [MarshalAs(UnmanagedType.LPStr)] string devicename, int bufferLength); public static void Main(String[] args) { CameraManager cm = new CameraManager(); } public CameraManager() { //let's test this CapParams cparamsTest = new CapParams(); cparamsTest.mHeight = 100; cparamsTest.mWidth = 100; cparamsTest.mTargetBuf = new int[1000]; System.Console.WriteLine(countCaptureDevices()); System.Console.WriteLine(initCapture(0, cparamsTest)); doCapture(0); while(isCaptureDone(0) <= 0) { } deinitCapture(0); for(int x = 0; x < 1000; x++) { System.Console.Write(cparamsTest.mTargetBuf[x] + " "); } System.Console.WriteLine("Done"); System.Console.ReadLine(); } } }
Advertisement
When you are importing a function from the dll, you aren't specifying an entrypoint.

[DllImport("escapi.dll", EntryPoint = "NameOfFunctionFromDllHere"]

Check out the following page

Strength without justice is no vice; justice without strength is no virtue.
Thanks. I fixed that. Unfortunately, it still doesn't work. I'm convinced I'm doing something wrong with type marshaling. Here's my updated code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace RobotServer
{
class CameraManager
{
[StructLayout(LayoutKind.Sequential)]
struct CapParams
{
[MarshalAs(UnmanagedType.SafeArray)]
public int[] mTargetBuf;

public int mHeight;
public int mWidth;
}

[DllImport("escapi.dll", EntryPoint = "countCaptureDevices")]
private static extern int countCaptureDevices();

[DllImport("escapi.dll", EntryPoint = "initCapture")]
private static extern int initCapture(uint deviceno, CapParams capParams);

[DllImport("escapi.dll", EntryPoint = "deinitCapture")]
private static extern void deinitCapture(uint deviceno);

[DllImport("escapi.dll", EntryPoint = "doCapture")]
private static extern void doCapture(uint deviceno);

[DllImport("escapi.dll", EntryPoint = "isCaptureDone")]
private static extern int isCaptureDone(uint deviceno);

[DllImport("escapi.dll", EntryPoint = "getCaptureDeviceName")]
private static extern void getCaptureDeviceName(uint deviceno, [MarshalAs(UnmanagedType.LPStr)] string devicename, int bufferLength);

public static void Main(String[] args)
{
CameraManager cm = new CameraManager();
}

public CameraManager()
{
//let's test this
CapParams cparamsTest = new CapParams();

cparamsTest.mHeight = 100;
cparamsTest.mWidth = 100;
cparamsTest.mTargetBuf = new int[1000];

System.Console.WriteLine(countCaptureDevices());

System.Console.WriteLine(initCapture(0, cparamsTest));

doCapture(0);

while(isCaptureDone(0) <= 0) { }

deinitCapture(0);

for(int x = 0; x < 1000; x++)
{
System.Console.Write(cparamsTest.mTargetBuf[x] + " ");
}

System.Console.WriteLine("Done");

System.Console.ReadLine();
}
}
}
Marshalling CapParams.mTargetBuf as UnmanagedType.SafeArray is incorrect. SafeArray corresponds to the unmanaged SAFEARRAY, which is a particular structure that describes arrays.

Have you tried LPArray instead?
[StructLayout(LayoutKind.Sequential)]struct CapParams{  [MarshalAs(UnmanagedType.LPArray)]  public int[] mTargetBuf;  public int mHeight;  public int mWidth;};

If you've got the original .h file then this program is a god send:

pInvoke Wizard


Cheers,

James
I'm looking into that wizard right now. I tried changing the SafeArray to LPArray and now it gets a runtime error "
Cannot marshal field 'mTargetBuf' of type 'CapParams': Invalid managed/unmanaged type combination (Arrays fields must be paired with ByValArray or SafeArray)".

If the wizard works, that's great. However, If I don't understand how to do it manually I'm dependent on the software and I really don't want to shell out the cash to buy it right now.

[edit]
Btw, the PInvoke Wizard doesn't run on my computer because I don't have .NET Framework 1.1. I have .NET Framework 2.0 installed but it doesn't seem to be compatible with that. If you have it installed, do you think you could run my .h header file through it?
[/edit]

Thanks for taking the time to respond. --Alex
Hey, you should be able to grab yourself a copy .Net 1.1. Have a look around and see what you find. Failing that, PM me and I'll have a look at the .h file for you.

This topic is closed to new replies.

Advertisement