[API] Delegates and Parameters

M

Mythran

I am trying to get an API (EnumResourceNamesW - kernel32) to pass a specific
parameter to the call-back function. The declaration I currently have for
EnumResourceNamesW is:

[
DllImport(
"kernel32.dll",
EntryPoint = "EnumResourceNamesW",
CharSet = CharSet.Unicode,
SetLastError = true
)
]
static extern bool EnumResourceNamesWithID(
IntPtr ModuleHandle,
uint Type,
EnumResNameDelegate EnumFunc,
IntPtr Param
);

I have a delegate as follows:

private delegate bool EnumResNameDelegate(
IntPtr ModuleHandle,
IntPtr Type,
IntPtr Name,
IntPtr Param
);

I want to pass an ArrayList to the EnumResourceNamesWithID and have it pass
that to the EnumResNameDelegate method. But, since Param is an IntPtr, I
can't. Is there a way to do what I am trying to accomplish?

Currently, I don't pass anything. I have a member variable in the class and
am using this var to store the values of the resources found. But I want to
do this w/o a member variable. Any suggestions/help?

Thanks,
Mythran
 
N

Nicholas Paldino [.NET/C# MVP]

Mythran,

First off, your declaration for your delegate and the API is wrong.
They should be:

[DllImport("kernel32.dll", EntryPoint="EnumResourceNamesW",
CharSet=CharSet.Unicode, SetLastError=true)]
static extern bool EnumResourceNamesWithID(
IntPtr hModule,
string lpszType,
EnumResNameProcDelegate lpEnumFunc,
IntPtr lParam);

public delegate bool EnumResNameProcDelegate(
IntPtr hModule
[MarshalAs(UnmanagedType.LPWStr)]
string lpszType,
[MarshalAs(UnmanagedType.LPWStr)]
string lpszName,
IntPtr lParam);

Note the changing of the lpszType parameter to a string.

Now, to pass the array list, you would use a GCHandle structure, like
so:

// Assume the arraylist is in a variable named al.
GCHandle handle = GCHandle.Alloc(al);

Then make the call to EnumResourceNamesWithID. Pass the GCHandle in the
lParam parameter. It might require a specific cast to IntPtr.

In your callback, you want to cast the IntPtr to a GCHandle, and then
get the Target property, like so:

// Get the handle.
GCHandle handle = (GCHandle) lParam;

// Get the array list.
ArrayList al = (ArrayList) handle.Target;

Hope this helps.
 
M

Mythran

Nicholas Paldino said:
Mythran,

First off, your declaration for your delegate and the API is wrong.
They should be:

[DllImport("kernel32.dll", EntryPoint="EnumResourceNamesW",
CharSet=CharSet.Unicode, SetLastError=true)]
static extern bool EnumResourceNamesWithID(
IntPtr hModule,
string lpszType,
EnumResNameProcDelegate lpEnumFunc,
IntPtr lParam);

public delegate bool EnumResNameProcDelegate(
IntPtr hModule
[MarshalAs(UnmanagedType.LPWStr)]
string lpszType,
[MarshalAs(UnmanagedType.LPWStr)]
string lpszName,
IntPtr lParam);

Note the changing of the lpszType parameter to a string.

Now, to pass the array list, you would use a GCHandle structure, like
so:

// Assume the arraylist is in a variable named al.
GCHandle handle = GCHandle.Alloc(al);

Then make the call to EnumResourceNamesWithID. Pass the GCHandle in
the lParam parameter. It might require a specific cast to IntPtr.

In your callback, you want to cast the IntPtr to a GCHandle, and then
get the Target property, like so:

// Get the handle.
GCHandle handle = (GCHandle) lParam;

// Get the array list.
ArrayList al = (ArrayList) handle.Target;

Hope this helps.

Mattias Sjogren stated in a message dated Feb 20 2003,

"You can't use string as the type for lpszType/lpszName parameters in
these callback delegates, becuase they often deal with integers cast
to LPTSTR values, that can't be converted to a string my the
marshaler. You have to use IntPtr instead, and then figure out
yourself if it's an integer or a string pointer."

I tried it with string types, but an exception was being thrown that stated
"Invalid access to memory location." By applying the fix for that from the
message Mattias wrote and including the GCHandle.Alloc method for allocating
the handle for the array list, I was able to get this working successfully!

Thank you very much :)

Mythran
 

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