How to convert ISAPI related methods and structures to C# - pinvoke

I have an ISAPI filter written for IIS6. I now need to write a wrapper for IIS7 to wrap the IIS6 filter. I plan to write HTTP module in C# and Pinvoke the unmanaged dll methods.
I need C# representation of the following code,
DWORD WINAPI HttpFilterProc(
PHTTP_FILTER_CONTEXT pfc,
DWORD notificationType,
LPVOID pvNotification
);
typedef struct _HTTP_FILTER_CONTEXT HTTP_FILTER_CONTEXT {
DWORD cbSize;
DWORD Revision;
PVOID ServerContext;
DWORD ulReserved;
BOOL fIsSecurePort;
PVOID pFilterContext;
BOOL GetServerVariable;
BOOL AddResponseHeaders;
BOOL WriteClient;
VOID * AllocMem;
BOOL ServerSupportFunction;
} HTTP_FILTER_CONTEXT, * PHTTP_FILTER_CONTEXT;
I tried using PInvoke Assistant from codeplex but i am not able to make it work.
Has anyone done anything like this before?
Can anyone provide a solution to the above?
Correction: Correct structure added

Building on the code in your answer you need to use the following:
[DllImport(#"XyzISAPI.dll")]
public static extern uint HttpFilterProc(
ref HttpFilterContext pfc,
uint notificationType,
IntPtr pvNotification
);
The native code is passed a pointer to the context struct and passing the struct by ref is the easy way to match that. The final parameter is LPVOID, which is void* and that is plain IntPtr in managed code.
As for HTTP_FILTER_ACCESS_DENIED, define it like this:
[StructLayout(LayoutKind.Sequential)]
public struct HttpFilterAccessDenied
{
IntPtr URL;
IntPtr PhysicalPath;
uint Reason;
}
You can then obtain one of those like this:
HttpFilterAccessDenied hfad = (HttpFilterAccessDenied)Marshal.PtrToStructure(
pvNotification, typeof(HttpFilterAccessDenied));
And then you can get hold of the string values from the struct with Marshal.PtrToStringUni or Marshal.PtrToStringAnsi.

Related

Marshalling structures containing byte array

I have been struggling 2nd day with the task how to pass a structure, containing array of byte to c++ dll. Namely, I don't know how to marshal C# structure. Here is my code:
in C++ dll:
struct Image
{
int Width;
int Height;
int Depth;
uchar *Data;
};
.....
__declspec(dllexport) void DirectTransform(Image *InImage, Image*DestImage)
{
...Implementation...
}
in C# program:
[StructLayout(LayoutKind.Sequential)]
struct ImageData
{
public int Width;
public int Height;
public int Depth;
[MarshalAs(UnmanagedType.LPArray)]
public byte[] Data;
}
[DllImport("MyDll.dll",CallingConvention=CallingConvention.Cdecl)]
public static extern void DirectTransform(ImageData Src, ImageData Dest);
//Fill out both structures..
DirectTransform(Image, DestImage);
Exception throws at calling of DirectTransform and says that:
Cannot marshal field 'Data' of type'ImageData': Invalid managed/unmanaged
type combination (Array fields must be paired with ByValArray or
SafeArray).
When I change LPArray to ByValArray and point the size of array(in this case 202500) it also doesn't work, because the size is too large. When SafeArray is used, the program fails inside DLL with the message :
Attempted to read or write protected memory. This is often an
indication that other memory is corrupt.
And data in structures are erroneous.
Could anyone help me?
Look at this question
You can try passing a pointer to your array and, for example an integer length field.
Doing that, you'll need to manually allocate unmanaged memory using Marshal.AllocHGlobal, call your function and after that in finally block - clear after yourself with Marshal.FreeHGlobal
With Marshal.Copy method you are copying your data into allocated memory.

What are the RemoteRead and RemoteWrite members of ISequentialStream?

I am developing an COM library that uses the IStream interface to read and write data. My MIDL code looks like this:
interface IParser : IUnknown
{
HRESULT Load([in] IStream* stream, [out, retval] IParsable** pVal);
};
Since IStream and it's base interface ISequentialStream are not defined inside a type library, they get defined in mine. So far so good. However, when I view my type library with OLEView, ISequentialStream only defines the members RemoteRead and RemoteWrite, while I expected Read and Write, since they are what I am actually calling. Even more strange is, that the the MSDN lists those two members (additionally to the original ones), but states they are not supported.
The question
So what are those members and how do I use them from a client (e.g. a managed application to create a managed Stream wrapper for IStream)?
The long story
I want to implement a wrapper on the client side, that forwards IStream calls to .NET streams, like System.IO.FileStream. This wrapper could inherit from IStream like so:
public class Stream : Lib.IStream
{
public System.IO.Stream BaseStream { get; private set; }
public Stream(System.IO.Stream stream)
{
this.BaseStream = stream;
}
// All IStream members in here...
public void Read(byte[] buffer, int bufferSize, IntPtr bytesReadPtr)
{
// further implementation...
this.BaseStream.Read();
}
}
And then, I want to call my server with this wrapper:
var wrapper = new Stream(baseStream);
var parsable = parser.Load(wrapper);
The problem is, that Lib.Stream in the previous example only provides RemoteRead and RemoteWrite, so that server calls to stream->Read() would end up in no mans land. As far as I understood, there is System.Runtime.InteropServices.ComTypes.IStream for managed COM servers, but in my example I have a unmanaged COM server and a managed client that should provide IStream instances.
Actually, there is no RemoteRead and RemoteWrite in ISequentialStream v-table layout. They exist only in ObjIdl.Idl, as an aid for RPC proxy/stub code generator. Have a look at ObjIdl.h from SDK:
MIDL_INTERFACE("0c733a30-2a1c-11ce-ade5-00aa0044773d")
ISequentialStream : public IUnknown
{
public:
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Read(
/* [annotation] */
__out_bcount_part(cb, *pcbRead) void *pv,
/* [in] */ ULONG cb,
/* [annotation] */
__out_opt ULONG *pcbRead) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Write(
/* [annotation] */
__in_bcount(cb) const void *pv,
/* [in] */ ULONG cb,
/* [annotation] */
__out_opt ULONG *pcbWritten) = 0;
};
It's hard to guess why your type library ends up with RemoteRead/RemoteWrite names, instead of Read/Write. You may want to upload your IDL somewhere and post a link to it, if you need help with that.
However, as long as the v-table layout, the method signatures and the GUID of the interfaces from your typelib match those of ISequentialStream and IStream from ObjIdl.h, the method names do not matter.
Anyway, I would do as Igor suggested in his comment. Do not expose IStream at all in the type library. Use IUnknown in the IDL, and just cast it to System.Runtime.InteropServices.ComTypes.IStream inside the C# client's method implementation, when you actually do read/write, i.e.:
IDL:
interface IParser : IUnknown
{
HRESULT Load([in] IUnknown* stream, [out, retval] IParsable** pVal);
};
C#:
IParsable Load(object stream)
{
// ...
var comStream = (System.Runtime.InteropServices.ComTypes.IStream)stream;
comStream.Read(...);
// ...
}
[UPDATE] I guess I see what's going on with the method names. Your situation is exactly like this:
https://groups.google.com/forum/#!topic/microsoft.public.vc.atl/e-qj0xwoVzg/discussion
Once more, I suggest to not drag non-automation compatible interfaces into the type library, and I'm not alone here with this advice. You actually drag a lot more unneeded stuff into your typlib, which projects to the C# side too. Stick with IUnknown and make your typelib neat. Or, at last, define your own binary/GUID compatible versions of them from scratch.

Why do libgit2 methods using kernel32.dll's GetProcAddress always return 0?

I have the need to manually handle the loading / unloading of the actual git2.dll, instead of using [DllImport("git2")] in C#. I seem to have issues with creating an IntPtr for reference to the address of methods stored in libgit2.
Here're the good bits from my PluginManager class which are supposed to help facilitate manually loading, marshal/delegate (whenever I get this kink fixed), and unloading libraries.
public class PluginManager {
public const string LIB = "Assets\\Plugins\\git2.dll";
[DllImport( "kernel32.dll", CharSet = CharSet.Ansi )]
public static extern IntPtr LoadLibrary( [In, MarshalAs( UnmanagedType.LPStr )] string lib );
[DllImport( "kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true )]
public static extern IntPtr GetProcAddress( [In] IntPtr reference, [In, MarshalAs( UnmanagedType.LPStr )] string method );
[DllImport( "kernel32.dll" )]
public static extern bool FreeLibrary( [In] IntPtr reference );
}
Here's where I attempt to use them:
//# Working; always non-zero.
IntPtr reference = PluginManager.LoadLibrary( PluginManager.LIB );
//# Should be valid -- LibGit2Sharp.Core.NativeMethods.cs calls this method, too.
//# Always returns IntPtr.Zero.
IntPtr methodReference = PluginManager.GetProcAddress( reference, "git_repository_open" );
Is the library not exposed to this type of loading? I've tried all kinds of methods present in the LibGit2Sharp native hooks, but they always return zero.
It seems that git2.dll has mangled export names, so git_repository_open is actually _git_repository_open#4. After researching some more on why this is happening, it seems that, according to This Post, and several others that I've run into while looking for a fix, that the functions may not be exported using extern "C" or may not be using a .def file.
I did look through some of the libgit2 code, and I saw extern used on a couple functions, but on others I saw no export keyword at all, but they're still being accessed externally from LigGit2Sharp's NativeMethods.cs class.
While all of that is foreign to me, I did find a very useful post about a tool shipped with Visual Studio that allows you to see all exported items, and their names: Dumpbin. With this, I was able to determine the exported names, and used the /OUT:filename flag to save them to a file: PasteBin
With all of the exported names at my disposal, I just have to follow function calls, that I'm actually using, and replace the the methods in NativeMethods.cs to look something like this: LibGit2Sharp Issue #341.

Unable to find an entry point

I import two WinApi functions and use them in my class
using namespace System::Runtime::InteropServices;
[DllImport("user32",ExactSpelling = true)]
extern bool CloseWindow(int hwnd);
[DllImport("user32",ExactSpelling = true)]
extern int FindWindow(String^ classname,String^ windowname);
public ref class WinApiInvoke
{
public:
bool CloseWindowCall(String^ title)
{
int pointer = FindWindow(nullptr,title);
return CloseWindow(pointer);
}
};
Then I create object in main program and call CloseWindowCall method
Console::WriteLine("Window's title");
String ^s = Console::ReadLine();
WinApiInvoke^ obj = gcnew WinApiInvoke();
if (obj->CloseWindowCall(s))
Console::WriteLine("Window successfully closed!");
else Console::WriteLine("Some error occured!");
When I write in console for example Chess Titans to close I've got an error
Unable to find an entry point named 'FindWindow' in DLL 'user32'
What entry point?
You are using the ExactSpelling property inappropriately. There is no FindWindow function in user32.dll, just like the exception message says. There is FindWindowA and FindWindowW. The first one handles legacy 8-bit character strings, the second uses Unicode strings. Any Windows api function that accepts strings has two versions, you don't see this in C or C++ code because the UNICODE macro selects between the two.
Avoid ExactSpelling on winapi functions, the pinvoke marshaller knows how to deal with this. You have some other mistakes, the proper declaration is:
[DllImport("user32.dll", CharSet = CharSet::Auto, SetLastError = true)]
static IntPtr FindWindow(String^ classname, String^ windowname);

Run-Time Check Failure #0 vb.net callback from C dll

I'm writing Add-inn Application A in VB.Net and DLL B in C language.
Application A pass callback method to dll B.
When certain event occur the dll invoke the callback from A.
Whole works fine on my PC but when I move it to Notebook I get an error:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
This is part of C code:
typedef void (__cdecl * OFFICE_PTR)();
void TAPIClient::tapiCallBack(
DWORD hDevice,
DWORD dwMessage,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3){
switch (dwMessage)
{
case LINE_CALLSTATE:
switch (dwParam1)
{
case LINECALLSTATE_OFFERING:
if(dwInstance!=NULL)
{
try
{
OFFICE_PTR vbFunc =(OFFICE_PTR)dwInstance;
vbFunc( );//Critical moment
}
catch(...)
{
MessageBox (NULL, L"( (OFFICE_PTR)dwInstance )(&sCallNr)",L"ERROR",MB_OK);
}
}
break;
};
break;
}
}
Where dwInstance is a address of application A callback method
This is part of VB.Net code:
Public Class TapiPlugin
Public Delegate Sub P_Fun()
Private Declare Function startSpy _
Lib "TAPIClient.dll" _
(ByVal pFun As P_Fun) As IntPtr
Public Shared Sub simpleTest()
MsgBox("Plugin sub simpleTest")
End Sub
Public Sub onStart()
Dim pBSTR As IntPtr
pBSTR = startSpy(AddressOf simpleTest)
MsgBox(Marshal.PtrToStringAuto(pBSTR))
Marshal.FreeBSTR(pBSTR)
End Sub
End Class
The Error occur when I try call 'vbFunc( )'. I would be grateful for any help. :D
If the calling convention is cdecl, then you need to declare your delegate like this:
<UnmanagedFunctionPointer(CallingConvention.Cdecl)>
Public Delegate Sub P_Fun()
You can only do this in .NET 2.0 and after, as the attribute was not introduced before then (and the interop layer was not changed to acknowledge it before that).
If the calling convention is indeed stdcall then the delegate can remain as is. You said it is stdcall, but I have doubts, since the exception is explicitly telling you that there might be a mismatch in calling conventions.
Do the two computers have different pointer sizes perhaps? Maybe one is a 64 bit machine and the other only 32?
typedef void (__cdecl * OFFICE_PTR)();
void TAPIClient::tapiCallBack(
DWORD hDevice,
DWORD dwMessage,
DWORD dwInstance,
...){
...
OFFICE_PTR vbFunc =(OFFICE_PTR)dwInstance;
vbFunc( );//Critical moment
The DWORD type is not really valid for passing pointer types. You should be using INT_PTR I guess.
I thing it is not a reason to check it out I passed the callback as global pointer of type OFFICE_PTR and i get the same result. On PC it work fine on Notebook it crash :(
A have to apologies for a mistake I wrote that the def look like:
typedef void (__cdecl * OFFICE_PTR)();
but for real it looks like
typedef void (__stdcall * OFFICE_PTR)();