I want to pass a struct as parameters from my C# application to a function in a C++ dll. The method is called correctly and works fine but ignores all set params and always runs with the default params.
In C++ the struct is defined as follows:
[DllImport("cgm2vector_api.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern CGM_STATUS cgm2vector(
[MarshalAs(UnmanagedType.LPWStr)] string inputFile,
[MarshalAs(UnmanagedType.LPWStr)] string outputFile,
[MarshalAs(UnmanagedType.LPWStr)] string logDest,
[MarshalAs(UnmanagedType.LPWStr)] string configFile,
ref CGM2VECTOR_PARAMS @params);
public static CGM_STATUS Convert(string inputFile, string outputFile, int units, string logDest = null, string configFile = null)
{
var u = (CGM2VECTOR_UNITS)units;
var @params = new CGM2VECTOR_PARAMS
{
width = 0,
height = 0,
units = (int)u,
demoflag = 0,
lineScale = 1.0,
logMode = 0,
uncompressed = 0,
colorMode = 0,
reverseVideo = 0,
mediaColor = 0,
boundsCheckRatio = 0,
outputType = 2,
marginSize = [0, 0],
marginUnits = (int)CGM2VECTOR_UNITS.CGM2VECTOR_PTS,
lineWidthMin = -1,
lineWidthMax = -1,
extentCheckMargin = 0,
CSSformat = 0,
genRegions = 0,
seqIdentifiers = 0,
usePaths = 0,
rotation = 0
};
return Cgm2VectorApi.cgm2vector(inputFile, outputFile, logDest, configFile, ref @params);
}
Here is my param struct. I also tried LayoutKind.Explicit but it did not change anything
[StructLayout(LayoutKind.Sequential)]
public struct CGM2VECTOR_PARAMS
{
public double width;
public double height;
public int units;
public long demoflag;
public double lineScale;
public long logMode;
public int uncompressed;
public long colorMode;
public long reverseVideo;
public long mediaColor;
public float boundsCheckRatio;
public int outputType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public float[] marginSize;
public long marginUnits;
public double lineWidthMin;
public double lineWidthMax;
public float extentCheckMargin;
public int CSSformat;
public int genRegions;
public int seqIdentifiers;
public int usePaths;
public double rotation;
}
The h file of the c++ methods looks likes this.
#pragma once
#ifdef WIN32
# define CALL_TYPE __stdcall
#else
# define CALL_TYPE
#endif /* WIN32 */
#ifdef __cplusplus
#define EXTERN
extern "C" {
#else
#define EXTERN extern
#endif /*__cplusplus*/
/* Status return code used by all of the CGM entry points. */
#ifndef CGM_STATUS_DEFINED
#define CGM_STATUS_DEFINED
typedef enum {
CGM_STATUS_OK,
CGM_STATUS_ERROR,
CGM_STATUS_FILE_DOES_NOT_EXIST,
CGM_STATUS_ERROR_OPENING_FILE,
CGM_STATUS_NOT_CGM,
CGM_STATUS_ARG_ERROR,
CGM_STATUS_TAKES_TOO_MUCH_MEMORY,
CGM_STATUS_IS_CHARACTER, // cgm is character encoded which is not supported
CGM_STATUS_EVAL_EXPIRED, // evaluation period has expired
CGM_STATUS_EXTENT_ERROR, // check if graphical extent extends beyond picture extent.
CGM_STATE_ERROR, // caused by out of sequence control element
CGM_TOO_MANY_UNRECOGNIZED, // > 10 undefined element ids usually indicating corrupt cgm file
CGM_STATUS_EXCEPTION_ERROR // try-catch exception
} CGM_STATUS;
#endif
typedef enum {
CGM2VECTOR_INCHES,
CGM2VECTOR_CMS,
CGM2VECTOR_PTS,
CGM2VECTOR_PERCENT,
CGM2VECTOR_MM
} CGM2VECTOR_UNITS;
typedef struct CGM2VECTOR_PARAMS {
double width; // page width in inches, centimeters, millimeters, points, or points
double height; // page height in inches, centimeters, millimeters, points, or points
/* page size logic...
[0] > 0. && [1] > 0. - page width and page height as specified
[0] = 0. && [1] = 0. - page width and height will be CGM dimensions
[0] < 0. && [1] < 0. - shrink to fit within [0]-[1] maintaining CGM aspect ratio
[0] < 0. && [1] = 0. - shrink to fit [0] maintaining CGM aspect aspect ratio
[0] < 0. && [1] > 0. - force page height to [1], if width < abs([0]) OK... else ?
[0] = 0. && [1] < 0. - shrink to fit [1] maintaining aspect
[0] = 0. && [1] > 0. - force page height to [1] maintaining aspect
[0] > 0. && [1] < 0. - force page width to [0], if height < abs(height) OK... else ?
[0] > 0. && [1] = 0. - force page width to [0] maintaining aspect */
int units; /* page width & height units:
CGM2VECTOR_INCHES - specified in inches
CGM2VECTOR_CMS - specified in centimeters
CGM2VECTOR_PTS - specified in points
CGM2VECTOR_PERCENT - specified in percent
CGM2VECTOR_MM - specified in millimeters */
long demoflag; // insert demo banner in output: 0= no demo, 1= add
double lineScale; // line width scale factor
long logMode; // 0= no log, 1= log to console/file
int uncompressed; // image data compression- 0= PNG compressed, 1= uncompressed
long colorMode; // 0= do nothing, 1= greyscale, 2= greyscale enhanced (turn nonwhite/non fills to black)
long reverseVideo; // 0= do nothing, 1= reverse black and white
long mediaColor; // 0= do nothing, 1= black, 2= white, 3= transparent
float boundsCheckRatio; /* use to remove excessive whitespace:
if area of visible bounds covers less then this fraction of
the area of CGM (VDC) extent then the visible bounds (plus 5% or
margins if specified) will be used as the CGM (VDC) extent */
int outputType; // 0= PDF, 1= EPS, 2= SVG (3= WIN32-Cairo 4=WIN32-GDI 5=WIN32-GL)
float marginSize[2];/* margin size: [0] = width, [1] = height in INCHES, CM, POINTS, PERCENT, or MM */
long marginUnits; /* margin size units:
CGM2VECTOR_INCHES - margin size specified in inches
CGM2VECTOR_CMS - margin size specified in centimeters
CGM2VECTOR_PTS - margin size specified in points
CGM2VECTOR_MM - margin size specified in millimeters */
double lineWidthMin; // line width minimum
double lineWidthMax; // line width maximum
float extentCheckMargin; // 0= do not check extent, >0= generate error if graphical extent extends beyond CGM picture extent
// + margin. Example: .05 if graphical content extends beyond picture extent + 5 percent margin
int CSSformat; // for SVG outputType; 0=use inline styling (larger file size, but easier to join),
// !0=use internal CSS (produces smaller fill size, harder to join)
int genRegions; // generate hotspots regions when grobject is missing region attribute but has name attribute
int seqIdentifiers; // for SVG; 0=generate random identifier for clip path Ids for easier merging
// 1=use sequential identifier for clip path Ids (to reproduce exact output images)
int usePaths; // 0= convert polylines and polygons to svg polyline (line for 2 points) and polygon elements
// 1= use <path> elements instead to reduces svg file size
double rotation; // rotate image output, acceptable values are 0., 90., 180., 270.
} CGM2VECTOR_PARAMS;
CGM_STATUS CALL_TYPE cgm2vector(
const wchar_t *input_file, // [in] CGM input file path
const wchar_t *output_file, // [in] vector output file path
wchar_t *log_dest, // [in] log destination (file path or memory pointer)
const wchar_t *config_file,
CGM2VECTOR_PARAMS *params);
#ifdef __cplusplus
}
#endif /*__cplusplus*/
Why does it always run with default params? I would have expected if something was wrong with my wrapper to not run at all.
In C#, long
is a keyword for .NET's System.Int64, which represents a 64-bit signed integer.
In C++, long's size can vary but it's often a 32-bit signed integer (check your compiler).
So if your C++ long
's size is indeed 32-bit, you must use C#'s int
(or .NET System.Int32
) otherwise you'll get an offset mismatch in the structure and anything can and will happen.
PS: .NET Marshal.SizeOf
, Marshal.OffsetOff
and C++ sizeof
, offsetof
can be used also to compare overall structures offsets and sizes.