LayoutKind.Explicit breaks service install

D

Dave

I have a structure declared as:

[StructLayout(LayoutKind.Explicit, Size=6)]
private struct StandardFrame
{
[FieldOffset(0)] public byte [] frame;
[FieldOffset(0)] public UInt32 integrityCheck;
[FieldOffset(2)] public UInt32 sequenceNo;
[FieldOffset(4)] public UInt32 dataLength;

}

This is part of a Windows Service C# app. I've added an installer and
setup project but the install fails with "Unable to get installer types
in the <name of executable> --> one or more of the types in the
assembly unable to load."

I've narrowed the problem down to this struct. If I make the struct
LayoutKind.Sequential and remove the FieldOffset values the service
installs no problem. Obviously this is not what I want, the struct is
to be used as a union. This has really got me stumped, I can work
around it but I would prefer to use this approach.

Any clues gratefully accepted.
 
N

Nicholas Paldino [.NET/C# MVP]

Dave,

Well, the first problem I see is that you set the size of the structure
to 6, and yet, you have an integer (four bytes) at offset four, which means
the size of the structure needs to be 8.

Are you sure that you dont want that offset to be at 2? Or the size is
not correct?

Hope this helps.
 
M

Michael C

Dave said:
I've narrowed the problem down to this struct. If I make the struct
LayoutKind.Sequential and remove the FieldOffset values the service
installs no problem. Obviously this is not what I want, the struct is
to be used as a union. This has really got me stumped, I can work
around it but I would prefer to use this approach.

Any clues gratefully accepted.

I dumped the installer from my service and install it using windows API when
the setup for my program is run.

Or, as nicholas said, your structure is pretty screwed up.

Michael
 
D

Dave

Good point, must have been having a bad day. So my structure is now
hopefully correct:

[StructLayout(LayoutKind.Explicit, Size=6)]
private struct StandardFrame
{
[FieldOffset(0)] public byte [] frame;
[FieldOffset(0)] public UInt16 integrityCheck;
[FieldOffset(2)] public UInt16 sequenceNo;
[FieldOffset(4)] public UInt16 dataLength;
}

It is a 6-byte framing sequence that is a packet header for
communicating with a mainframe system.

Sadly fixing the structure declaration doesn't actually fix the
installer issue. When I run installutil against my service assembly it
throws an exception with the following call stack:

An exception occurred while trying to find the installers in the
c:\documents and settings\davec\my documents\visual studio
projects\hsmthriftlineservice\bin\debug\hsmthriftlineservice.exe
assembly.
System.Reflection.ReflectionTypeLoadException: One or more of the types
in the assembly unable to load.
at System.Reflection.Module.GetTypesInternal(StackCrawlMark&
stackMark)
at System.Reflection.Module.GetTypes()
at
System.Configuration.Install.AssemblyInstaller.GetInstallerTypes(Assembly
assem)
at
System.Configuration.Install.AssemblyInstaller.InitializeFromAssembly()

To me it appears to be trolling through the types in the assembly and
trips up on the struct.
 
D

Dave

Thanks for the response. Do you have an example of doing this that I
could take a look at? Do you still use the setup project to generate
the install? I'm pretty sure I can work around the struct issue which
might be more expedient than going down the route you're suggesting.
 
D

Dave

Actually it has been pointed out that quite apart from the types
declared in the struct being incorrect, it's not legal to overlap
reference and value types. Thanks anyway.
 
W

Willy Denoyette [MVP]

Dave said:
Actually it has been pointed out that quite apart from the types
declared in the struct being incorrect, it's not legal to overlap
reference and value types. Thanks anyway.

All you can do about this is flatten the byte[]...

[StructLayout(LayoutKind.Explicit, Size=6)]
struct S
{
[FieldOffset(0)]public byte b1;
[FieldOffset(1)]public byte b2;
[FieldOffset(2)]public byte b3;
[FieldOffset(3)]public byte b4;
[FieldOffset(4)]public byte b5;
[FieldOffset(5)]public byte b6;
[FieldOffset(0)]public short s1;
[FieldOffset(2)]public short s2;
[FieldOffset(4)]public short s3;
}

Willy.
 
M

Michael C

Dave said:
Thanks for the response. Do you have an example of doing this that I
could take a look at? Do you still use the setup project to generate
the install? I'm pretty sure I can work around the struct issue which
might be more expedient than going down the route you're suggesting.

Fixing the struct is probably required anyway so might be the best way to
go. I found using the API a better way to go in my case. I don't have a C#
sample because I did it as an addin for NSIS in visual C 6 but look up help
on CreateService, OpenSCManager, QueryServiceStatus, CloseServiceHandle and
ControlService. There is a good example in the help which I pretty much
copied.
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi Dave,


Regarding the struct, IMO the way to go is Willy's , you have to pay
attention to the byte ordering ( little/big endian) though.

Regarding the install, are you including an installer?
Right click the project in the project explorer and select Add an installer,
then add a setup project to the solution, add the primary output of your
project, and finally add a custom action (for all the 4 events) of your
primary output.
I had a link from MSDN explaining all this, but cannot find it right now
:( , will post it when I find it.


cheers,
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

Another suggestion, I would test ALL your code and communication from either
a windows or console app, only when you are sure it does work move it to a
windows service.


cheers,
 
C

Chris Dunaway

I have code in VB.Net to install a service using the API. If you're
interested, I can post it.
 
D

Dave

Wow, first thanks for all the responses.

Ignacio, you are correct, I should have tested the class containing the
structure before slapping it into a service. I was reusing code from an
existing service that didn't have a separate test harness so I was
winging it and got burnt.

Chris, yes I would be curious to see the service install using the API.
Is there a particular reason you went that route? Once I fixed the
struct issue the installer worked fine.

I guess the thing that really bit me was that I was able to declare the
invalid struct but it wasn't until runtime that my error was caught
(I'm talking about not being able to use explicit offsets with
reference types rather than specifying UInt32 instead of UInt16). This
probably reflects a poor development approach on my part, plus coming
from a c++ background I did tend to rely on the compiler to highlight
this kind of mistake. I will assume that due to the way attributes are
implemented this kind of error is difficult to pick up at compile time.

So the approach I'm now taking is to ditch the struct and use
Buffer.BlockCopy() to get the values I need. I would be curious also to
know if there is a better approach to doing this transformation.
Basically I need to convert between 3 UInt16's and 6 bytes as a header
for a data packet.

Thanks and regards
 
C

Chris Dunaway

Dave said:
Chris, yes I would be curious to see the service install using the API.
Is there a particular reason you went that route? Once I fixed the
struct issue the installer worked fine.

My reason for going that route is that I wanted the service be able to
install itself without the need for an external utility. So with my
code, I can just call progname /install to install the service or
progname /uninstall to uninstall it.

Check this link:

http://tinyurl.com/7382d
 
D

Dave

Nice, thanks for posting Chris. And yes I can see the advantage to
being able to install the traditional way.

Dave
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top