1

I have three struct's:

 [StructLayout(LayoutKind.Sequential)] internal struct COPYDATASTRUCT { public IntPtr dwData; // Specifies data to be passed public int cbData; // Specifies the data size in bytes public IntPtr lpData; // Pointer to data to be passed } public struct SHELLTRAYDATA { public UInt32 dwUnknown; public UInt32 dwMessage; public NID_XX nid; } public struct NID_XX { public UInt32 cbSize; public IntPtr hWnd; public uint uID; public uint uFlags; public uint uCallbackMessage; public IntPtr hIcon; } 

In my WndProc I do the following:

 case WM_COPYDATA: { COPYDATASTRUCT cp = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT)); if(cp.dwData == SH_TRAY_DATA) { var shellTrayData = (SHELLTRAYDATA)Marshal.PtrToStructure(cp.lpData,typeof(SHELLTRAYDATA)); HandleNotification(shellTrayData); } } 

When my application is running on x86 it works fine. When I run it on x64 I don't get an hIcon and moreover the hWnd is invalid. When I target the application to x86 and run on x64 it works fine though. I know the problem lies in Marshalling. Do I have to manually Marshal the structure? Need help on this. I would prefer to have the same struct for both x64 and x86

EDIT:

The Unmanaged structures are as follows:

typedef struct tagCOPYDATASTRUCT { ULONG_PTR dwData; DWORD cbData; _Field_size_bytes_(cbData) PVOID lpData; } COPYDATASTRUCT, *PCOPYDATASTRUCT; // data sent by shell via Shell_NotifyIcon typedef struct _SHELLTRAYDATA { DWORD dwUnknown; DWORD dwMessage; NID_XX nid; } *PSHELLTRAYDATA; // sub structure common to all others typedef struct { DWORD cbSize; HWND hWnd; UINT uID; UINT uFlags; UINT uCallbackMessage; HICON hIcon; } NID_XX, *PNID_XX; typedef const NID_XX * PCNID_XX; 

EDIT: Sizes of the structs are as follows:

Unmanaged:

  1. COPYDATASTRUCT: 12(X86) and 24(x64)
  2. SHELLTRAYDATA: 32(X86) and 48(X64)
  3. NID_XX: 24(X86) and 40(X64)

Managed:

  1. COPYDATASTRUCT: 12(X86) and 24(x64)
  2. SHELLTRAYDATA: 32(X86) and 48(X64)
  3. NID_XX: 24(X86) and 40(X64)

It's the same on both the sides.

8
  • 3
    The answer to this can be found by examining the unmanaged structure definitions. You did not present them. This is interop. Interop requires knowledge of both sides of the interface. We can only see one side. Commented Jan 20, 2015 at 12:48
  • 1) You probably need to either replace some IntPtrs with 32 bit integers, or the other way round. Compare the definition of these structs in C and C# and figure out the correct integer sizes using the table at Windows Data Types on MSDN. 2) Alignment differences are another potential problem, but shouldn't affect your case. Commented Jan 20, 2015 at 12:49
  • The other issue that I can see is that you may have different architectures here. You are using WM_COPYDATA right? Do you have a 32 bit process sending a message to a 64 bit process? Or vice versa? Commented Jan 20, 2015 at 13:06
  • Since my application targets 'Any CPU', my app is running as a 64 bit process and the tray icon is a 32 bit process. I'm aware of the different architectures and I have mentioned the same in my question as well. But in that case I have to manually Marshal? If yes, how? Commented Jan 20, 2015 at 13:14
  • I don't see an obvious mismatch. Maybe you used an unusual packing in the C++ code. Be sure that the sizeof() of the structures in C++ code exactly matches the Marshal.SizeOf() you get in C# code. Make sure you don't use this to interop between different processes. Commented Jan 20, 2015 at 13:49

2 Answers 2

1

Since Handles are 64bits on 64bit builds, while the structure we receive still contain 32bits. Changing IntPtr to Uint32 for hIcon and hWnd did the trick. Now this struct works on both x64 and x86 architectures. I always knew serialization into JSON was definitely deviating from my requirement. Thanks for all the help.

Sign up to request clarification or add additional context in comments.

Comments

0

These structs vary on different architectures. The IntPtr values are 32 bits wide, or 64 bits wide depending on the architecture. That has obvious consequences for the size of the members, and perhaps less obvious consequences for alignment.

There's no way that you can use the same struct irrespective of architecture. You need to be aware of the architecture of both processes. Whenever sender and receiver don't have the same architecture, then you are in trouble.

You could pick a fixed layout, using C# long instead of IntPtr. Types like HWND and HICON will always fit in a C# long. Although do beware of handle types that only have meaning in a particular process. For instance, HICON values won't have meaning once you send them to a different process.

Instead of using a fixed layout struct I think I would serialize. Serialize the data that you wish to send to JSON, for example. This can readily be packed up into a string and sent via WM_COPYDATA.

7 Comments

long dint help my cause.
You have to change the struct everywhere. Not enough to change it in C# code only. You need to change the unmanaged structs also.
The unmanaged struct was from a Win32 project and is only for my reference. I changed IntPtr to long in all the 3 structs mentioned above. Correct me if I'm wrong.
You need to change the unmanaged structs also. long is 64 bits. On x86 HWND, HICON etc. are 32 bits. Do you understand what I wrote in the answer? That's the first thing to get clear. If you are just changing code without a solid foundation of understanding you won't succeed.
I did understand the 32 bit and 64 bit difference. What I dint understand is that when I'm P/Invoking the unmanaged struct in .NET, how will changing the unmanaged struct help? I'm using the managed version of it. Could you demonstrate with a snippet? My Managed Struct is a separate application. My purpose is to get tray icon details in a custom shell of my own.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.