I am trying to implement an Icon Overlay Handler in C#.
So far I have come up with the following code.
After registering the dll with regasm I get icon overlays all over the place
just as requested.
The problem is that a different icon is displayed as overlay than the
iconFile I
pass in te GetOverlayInfo methode.
Running the whole thing through the debugger I can see that on entering the
GetOverlayInfo methode the iconFileBuffer
is already filled and although I change it the "older" iconFile is used.
This leads me to the conclusion that I dealing with a string marshalling
problem. Switching to StringBuilder doesn't correct it.
Anyone?
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace ShellExtension
{
public sealed class ShellInterop
{
private ShellInterop()
{
}
/*
* void SHChangeNotify(
* LONG wEventId,
* UINT uFlags,
* LPCVOID dwItem1,
* LPCVOID dwItem2);
*/
[DllImport("shell32.dll")]
public static extern void SHChangeNotify(
int eventID,
uint flags,
IntPtr item1,
IntPtr item2);
}
[ComVisible(false)]
[ComImport]
[Guid("0C6C4200-C589-11D0-999A-00C04FD655E1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IShellIconOverlayIdentifier
{
[PreserveSig] int IsMemberOf(
[MarshalAs(UnmanagedType.LPWStr)] string path,
uint attributes);
[PreserveSig] int GetOverlayInfo(
[MarshalAs(UnmanagedType.LPWStr)] string iconFileBuffer,
int iconFileBufferSize,
out int iconIndex,
out uint flags);
[PreserveSig] int GetPriority(
out int priority);
}
[ComVisible(true)]
[Guid("B8FA9E43-38E6-4654-8A13-FF905AD22CE5")]
public class MyIconOverlay:IShellIconOverlayIdentifier
{
#region IShellIconOverlayIdentifier Members
public int IsMemberOf(string path, uint attributes)
{
//Show everything with icon overlay
return 0; // S_OK
}
public int GetOverlayInfo(string iconFileBuffer, int
iconFileBufferSize, out int iconIndex, out uint flags)
{
iconFileBuffer = @"c:\overlay.ico";
iconIndex = 0;
flags = 1; // ISIOI_ICONFILE
return 0; // S_OK
}
public int GetPriority(out int priority)
{
priority = 0; // 0-100 (0 is highest priority)
return 0; // S_OK
}
#endregion
#region Registry
[ComRegisterFunction]
public static void Register(Type t)
{
RegistryKey rk =
Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersi
on\Explorer\ShellIconOverlayIdentifiers\_" + t.Name);
rk.SetValue(string.Empty, t.GUID.ToString("B").ToUpper());
rk.Close();
ShellInterop.SHChangeNotify(0x08000000, 0, IntPtr.Zero,
IntPtr.Zero);
}
public static void Unregister(Type t)
{
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentV
ersion\Explorer\ShellIconOverlayIdentifiers\_" + t.Name);
ShellInterop.SHChangeNotify(0x08000000, 0, IntPtr.Zero,
IntPtr.Zero);
}
#endregion
}
}
Hi Jan,
I'm not sure if the default marshaler will copy the modified string back to
the unmanaged memory area. In this situation I suggest you defining this
method as
[PreserveSig] int GetOverlayInfo(
IntPtr iconFileBuffer,
int iconFileBufferSize,
out int iconIndex,
out uint flags);
and doing marshalling by yourself.
public int GetOverlayInfo( IntPtr iconFileBuffer, int iconFileBufferSize,
out int iconIndex, out uint flags)
{
System.Diagnostics.Debug.WriteLine(string.Format("GetOverlayInfo::{0}",iconF
ileBuffer));
System.Diagnostics.Debug.WriteLine(string.Format("GetOverlayInfo::{0}",iconF
ileBufferSize));
string fname = @"c:\overlay.ico";
int bytesCount = System.Text.Encoding.Unicode.GetByteCount(fname);
System.Diagnostics.Debug.WriteLine(string.Format("GetOverlayInfo::{0}",bytes
Count));
byte[] bytes = System.Text.Encoding.Unicode.GetBytes(fname);
if (bytes.Length + 2 < iconFileBufferSize)
{
for(int i = 0; i < bytes.Length; i++)
{
Marshal.WriteByte(iconFileBuffer,i,bytes[i]);
}
//write the "\0\0"
Marshal.WriteByte(iconFileBuffer,bytes.Length,0);
Marshal.WriteByte(iconFileBuffer,bytes.Length+1,0);
}
iconIndex = 0;
flags = 1; // ISIOI_ICONFILE
return 0; // S_OK
}
Does it resolve your problem?
If you have any problem on this issue, please feel free to reply this
thread.
Thanks!
Best regards,
Ying-Shen Yu [MSFT]
Microsoft Community Support
Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties and confers no rights.
This mail should not be replied directly, please remove the word "online"
before sending mail.
Jan Willem Zondag - 28 May 2004 08:55 GMT
Hi Ying-Shen,
Problem resolved, thanks!
Thought it had to do with marshalling, had tried several options:
- marschalling with [String]
- marshalling with [StringBuilder]
- IntPtr iconFileBuffer = Marshal.StringToCoTaskMemUni("string")
- IntPtr iconFileBuffer = Marshal.StringToHGlobalUni("string")
forgot to use your solution: write to unmanaged memory area directly
Regards,
Jan
> Hi Jan,
>
[quoted text clipped - 12 lines]
> out int iconIndex, out uint flags)
> {
System.Diagnostics.Debug.WriteLine(string.Format("GetOverlayInfo::{0}",iconF
> ileBuffer));
System.Diagnostics.Debug.WriteLine(string.Format("GetOverlayInfo::{0}",iconF
> ileBufferSize));
> string fname = @"c:\overlay.ico";
>
> int bytesCount = System.Text.Encoding.Unicode.GetByteCount(fname);
System.Diagnostics.Debug.WriteLine(string.Format("GetOverlayInfo::{0}",bytes
> Count));
>
[quoted text clipped - 30 lines]
> This mail should not be replied directly, please remove the word "online"
> before sending mail.