Complex Marshalling (breaking??) changes in CF2.0 SP1???

  • Thread starter Functional Illiterate
  • Start date
F

Functional Illiterate

Hey all,

I am having a little bit of a problem with Complex Marshalling in
differing versions of CF2.0. I am trying to decide if it is me or the
CF 2.0 SP1. I've found a workaround, but the core problem is really
bugging me. So, here's a bit of background, the error I am seeing, my
questions, my workaround.

Background
------------------
Device has a ARMV4i processor. C# app interfaces with a smartcard
reader on the device through P/Invokes. With .NET CF 2.0 everything
worked fine. However, with .NET CF 2.0 SP1, the app throws an
unmanaged exception (0x8000 0002) on P/Invokes to SCardGetStatusChange
only when a smartcard is inserted.

This is where it gets interesting (to me at least, yet my world is
small). SCardGetStatusChange is in declared winscard.h as follows:

SCardGetStatusChange(
IN SCARDCONTEXT hContext,
IN DWORD dwTimeout,
IN OUT LPSCARD_READERSTATEW rgReaderStates,
IN DWORD cReaders);

Where LPSCARD_READERSTATEW is defined as

typedef struct {
LPCWSTR szReader; // reader name
LPVOID pvUserData; // user defined data
DWORD dwCurrentState; // current state of reader at time of
call
DWORD dwEventState; // state of reader after state change
DWORD cbAtr; // Number of bytes in the returned ATR.
BYTE rgbAtr[36]; // Atr of inserted card, (extra
alignment bytes)
} SCARD_READERSTATEW, *PSCARD_READERSTATEW, *LPSCARD_READERSTATEW;


Super. In C# we declare the managed complements as:

[DllImport("winscard.dll")]
public static extern uint SCardGetStatusChange(IntPtr hContext,
uint dwTimeout,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=3)]
SCARD_READERSTATE[] rgReaderState,
uint cReaders);

and

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct SCARD_READERSTATE
{
[MarshalAs(UnmanagedType.LPTStr)]
public string szReader;
public IntPtr pvUserData;
public uint dwCurrentState;
public uint dwEventState;
public uint cbAtr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=36)]
public byte[] rgbAtr;
}

(Thanks Fitim Skenderi).

Problem
-----------------
When a card is inserted in the smartcard reader, the byte array rgbAtr
gets populated with the ATR of the card.
In CF v2.0 a call to SCardGetStatusChange passing an array of
SCARD_READERSTATE structs works fine. However, CF v2.0 SP1 CANNOT
marshal the updated byte array back to managed code. Instead we get
the ExceptionCode 0x80000002 - a datatype misalignment.

Question
--------------
So, was the way we were P/Invoking incorrect from the begginning and
the CLR did not inform us, but SP1 it is correctly throwing an
exception????
Or, has the marshalling of complex structures been inadvertantly
changed in CF 2.0 SP1? Did somebody change the implementation so it's
writing byte for byte and incrementing a pointer rather than writing
along DWORD boundaries??
Is this a breaking change????

The app is once again working, it just required a small change. We
changed the declaration to the following:

public static extern uint SCardGetStatusChange(
IntPtr hContext,
uint dwTimeout,
IntPtr rgReaderState,
uint cReaders);

Now we do this fun stuff;
-allocate unmanged memory - Marshal.AllocHGlobal
-marshalling StructToPtr - Marshal.StructToPtr
-call SCardGetStatusChange
-Marshal PtrToStruct - Marshal.PtrToStruct
-free unmanaged memory - finally{ Marshal.FreeHGlobal}

The problem was worked around with a more forgiving alternative, but
any insight on what change introduced in SP1 would be greatly
appreciated.

Thanks,
IllFunc
 
R

Ryan Chapman [MSFT]

The marshaling code was changed between v2 and v2 SP1 to address some memory
leaks that we observed. The case you outline here could have been
affected--could you give me more detail about how you were using this API?
So far I can't reproduce it, but I'd like to get to the bottom of it.

Thanks,

Ryan Chapman
..NET Compact Framework

Functional Illiterate said:
Hey all,

I am having a little bit of a problem with Complex Marshalling in
differing versions of CF2.0. I am trying to decide if it is me or the
CF 2.0 SP1. I've found a workaround, but the core problem is really
bugging me. So, here's a bit of background, the error I am seeing, my
questions, my workaround.

Background
------------------
Device has a ARMV4i processor. C# app interfaces with a smartcard
reader on the device through P/Invokes. With .NET CF 2.0 everything
worked fine. However, with .NET CF 2.0 SP1, the app throws an
unmanaged exception (0x8000 0002) on P/Invokes to SCardGetStatusChange
only when a smartcard is inserted.

This is where it gets interesting (to me at least, yet my world is
small). SCardGetStatusChange is in declared winscard.h as follows:

SCardGetStatusChange(
IN SCARDCONTEXT hContext,
IN DWORD dwTimeout,
IN OUT LPSCARD_READERSTATEW rgReaderStates,
IN DWORD cReaders);

Where LPSCARD_READERSTATEW is defined as

typedef struct {
LPCWSTR szReader; // reader name
LPVOID pvUserData; // user defined data
DWORD dwCurrentState; // current state of reader at time of
call
DWORD dwEventState; // state of reader after state change
DWORD cbAtr; // Number of bytes in the returned ATR.
BYTE rgbAtr[36]; // Atr of inserted card, (extra
alignment bytes)
} SCARD_READERSTATEW, *PSCARD_READERSTATEW, *LPSCARD_READERSTATEW;


Super. In C# we declare the managed complements as:

[DllImport("winscard.dll")]
public static extern uint SCardGetStatusChange(IntPtr hContext,
uint dwTimeout,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=3)]
SCARD_READERSTATE[] rgReaderState,
uint cReaders);

and

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct SCARD_READERSTATE
{
[MarshalAs(UnmanagedType.LPTStr)]
public string szReader;
public IntPtr pvUserData;
public uint dwCurrentState;
public uint dwEventState;
public uint cbAtr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=36)]
public byte[] rgbAtr;
}

(Thanks Fitim Skenderi).

Problem
-----------------
When a card is inserted in the smartcard reader, the byte array rgbAtr
gets populated with the ATR of the card.
In CF v2.0 a call to SCardGetStatusChange passing an array of
SCARD_READERSTATE structs works fine. However, CF v2.0 SP1 CANNOT
marshal the updated byte array back to managed code. Instead we get
the ExceptionCode 0x80000002 - a datatype misalignment.

Question
--------------
So, was the way we were P/Invoking incorrect from the begginning and
the CLR did not inform us, but SP1 it is correctly throwing an
exception????
Or, has the marshalling of complex structures been inadvertantly
changed in CF 2.0 SP1? Did somebody change the implementation so it's
writing byte for byte and incrementing a pointer rather than writing
along DWORD boundaries??
Is this a breaking change????

The app is once again working, it just required a small change. We
changed the declaration to the following:

public static extern uint SCardGetStatusChange(
IntPtr hContext,
uint dwTimeout,
IntPtr rgReaderState,
uint cReaders);

Now we do this fun stuff;
-allocate unmanged memory - Marshal.AllocHGlobal
-marshalling StructToPtr - Marshal.StructToPtr
-call SCardGetStatusChange
-Marshal PtrToStruct - Marshal.PtrToStruct
-free unmanaged memory - finally{ Marshal.FreeHGlobal}

The problem was worked around with a more forgiving alternative, but
any insight on what change introduced in SP1 would be greatly
appreciated.

Thanks,
IllFunc
 
F

Functional Illiterate

Hey Ryan,

Thanks for the reply. Since the source code from which winscard.dll is
created is not available, I made a small test dll in C++. The test
dll exports a function which is declared in a similar fashion to
SCardGetStatusChange. In this function I simply update some values and
write to the byte array embedded in the SCARD_READERSTATE structure.
Here is the simple function:

SIMPLEDLL_API uint
ComplexStructArrayFunc(
SCARDCONTEXT hContext,
DWORD dwTimeout,
LPSCARD_READERSTATE scRead,
DWORD Readers)
{
unsigned int i, r = 0;
for (i = 0; i < Readers; i++) {
LPSCARD_READERSTATE scr = (scRead + i);
// Write values
(*scr).dwCurrentState = 10 * (i+1);
(*scr).dwEventState = 20 * (i+1);
(*scr).cbAtr = 30 * (i+1);

if (NULL != (*scr).rgbAtr) {
// Update ATR array to simulate getting ATR from
smartcard.
BYTE *b = (*scr).rgbAtr;
for (int j = 0; j < 5; j++) {
b[j] = j + (10*i);
}
}
}

return 0;
}

If I comment out writing to the byte array everything marshals in & out
just fine. When code above runs, I get a native exception. For this
code I don't get the 0x80000002, instead I get a 0xC0000005. I check
the mem address written to and every thing seems fine until the data
gets marshalled back up to managed code. For actual calls to
SCardGetStatusChange, I get the 0x8000 0002 exception when the rgbatr
array is written to.

And just for reference, the code above is called from this managed code
stub.
[DllImport("SimpleDll.dll", CharSet = CharSet.Auto,
SetLastError = true)]
private static extern uint ComplexStructArrayFunc(
IntPtr hContext,
int timeout,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex =
3)]
SCARD_READERSTATE[] rgReaderState,
uint size);

public static void TestComplexStructArrayMarshal() {
SCARD_READERSTATE[] s_array = new SCARD_READERSTATE[2];

for (int i = 0; i < s_array.GetLength(0); i++) {
s_array.szReader = "SmartCard Reader: " + i;
s_array.pvUserData = IntPtr.Zero;
s_array.rgbAtr = new byte[36];
}

System.Diagnostics.Debug.Assert(false, "About to P/Invoke");

uint a = ComplexStructArrayFunc( IntPtr.Zero, 10,
s_array, (uint)s_array.Length);

System.Diagnostics.Debug.Assert(false, "Re-entering Managed
Code");

uint result = s_array[0].dwCurrentState;
}

Like I mentioned before, we've already found a way around this issue,
but the Complex marshalling behavior is odd.

f(illiterate).
The marshaling code was changed between v2 and v2 SP1 to address some memory
leaks that we observed. The case you outline here could have been
affected--could you give me more detail about how you were using this API?
So far I can't reproduce it, but I'd like to get to the bottom of it.

Thanks,

Ryan Chapman
.NET Compact Framework

Functional Illiterate said:
Hey all,

I am having a little bit of a problem with Complex Marshalling in
differing versions of CF2.0. I am trying to decide if it is me or the
CF 2.0 SP1. I've found a workaround, but the core problem is really
bugging me. So, here's a bit of background, the error I am seeing, my
questions, my workaround.

Background
------------------
Device has a ARMV4i processor. C# app interfaces with a smartcard
reader on the device through P/Invokes. With .NET CF 2.0 everything
worked fine. However, with .NET CF 2.0 SP1, the app throws an
unmanaged exception (0x8000 0002) on P/Invokes to SCardGetStatusChange
only when a smartcard is inserted.

This is where it gets interesting (to me at least, yet my world is
small). SCardGetStatusChange is in declared winscard.h as follows:

SCardGetStatusChange(
IN SCARDCONTEXT hContext,
IN DWORD dwTimeout,
IN OUT LPSCARD_READERSTATEW rgReaderStates,
IN DWORD cReaders);

Where LPSCARD_READERSTATEW is defined as

typedef struct {
LPCWSTR szReader; // reader name
LPVOID pvUserData; // user defined data
DWORD dwCurrentState; // current state of reader at time of
call
DWORD dwEventState; // state of reader after state change
DWORD cbAtr; // Number of bytes in the returned ATR.
BYTE rgbAtr[36]; // Atr of inserted card, (extra
alignment bytes)
} SCARD_READERSTATEW, *PSCARD_READERSTATEW, *LPSCARD_READERSTATEW;


Super. In C# we declare the managed complements as:

[DllImport("winscard.dll")]
public static extern uint SCardGetStatusChange(IntPtr hContext,
uint dwTimeout,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=3)]
SCARD_READERSTATE[] rgReaderState,
uint cReaders);

and

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct SCARD_READERSTATE
{
[MarshalAs(UnmanagedType.LPTStr)]
public string szReader;
public IntPtr pvUserData;
public uint dwCurrentState;
public uint dwEventState;
public uint cbAtr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=36)]
public byte[] rgbAtr;
}

(Thanks Fitim Skenderi).

Problem
-----------------
When a card is inserted in the smartcard reader, the byte array rgbAtr
gets populated with the ATR of the card.
In CF v2.0 a call to SCardGetStatusChange passing an array of
SCARD_READERSTATE structs works fine. However, CF v2.0 SP1 CANNOT
marshal the updated byte array back to managed code. Instead we get
the ExceptionCode 0x80000002 - a datatype misalignment.

Question
--------------
So, was the way we were P/Invoking incorrect from the begginning and
the CLR did not inform us, but SP1 it is correctly throwing an
exception????
Or, has the marshalling of complex structures been inadvertantly
changed in CF 2.0 SP1? Did somebody change the implementation so it's
writing byte for byte and incrementing a pointer rather than writing
along DWORD boundaries??
Is this a breaking change????

The app is once again working, it just required a small change. We
changed the declaration to the following:

public static extern uint SCardGetStatusChange(
IntPtr hContext,
uint dwTimeout,
IntPtr rgReaderState,
uint cReaders);

Now we do this fun stuff;
-allocate unmanged memory - Marshal.AllocHGlobal
-marshalling StructToPtr - Marshal.StructToPtr
-call SCardGetStatusChange
-Marshal PtrToStruct - Marshal.PtrToStruct
-free unmanaged memory - finally{ Marshal.FreeHGlobal}

The problem was worked around with a more forgiving alternative, but
any insight on what change introduced in SP1 would be greatly
appreciated.

Thanks,
IllFunc
 
R

Ryan Chapman [MSFT]

Thanks for catching this. This does look like a regression from v2 to SP1.
This will be fixed in an upcoming service release (unfortunately we don't
have a date for the release yet). The bug will reproduce if a structure
with a string parameter followed by any non-string parameters is sent
through the marshaling code.

I think your workaround is the simplest and most effective, although you
could try to coerce the string into being marshaled as an IntPtr (you'd
probably end up jumping through all the same hoops, though).

Thanks again, and good luck!

Ryan Chapman
..NET Compact Framework
 
Top