|
Here is the progress so far, but I am not able to successfully get a valid file handle to the window and show it. It does not crash however. This will aid to your progress as well.
namespace MyApp
{
struct WNDCLASSEX
{
public uint cbSize;
public uint style;
public long lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public long hInstance;
public long hIcon;
public long hCursor;
public long hbrBackground;
public string lpszMenuName;
public string lpszClassName;
public long hIconSm;
}
class Class1
{
public const string KERNEL32_DLL = "kernel32.dll";
[DllImport(KERNEL32_DLL, EntryPoint="GetModuleHandle")]
static extern int GetModuleHandleA(string lpModuleName);
public const string USER32_DLL = "user32";
public const uint CS_VREDRAW = 0x0001;
public const uint CS_HREDRAW = 0x0002;
public const long WS_OVERLAPPED = 0x00000000L;
public const long WS_EX_APPWINDOW = 0x00040000L;
public const long WS_SYSMENU = 0x00080000L;
public const int SW_SHOWNORMAL = 1;
[DllImport(USER32_DLL, EntryPoint="MessageBox")]
public static extern int MessageBox(int hWnd, String strMessage, String strCaption, uint uiType);
[DllImport(USER32_DLL, EntryPoint="GetForegroundWindow")]
public static extern int GetForegroundWindow();
[DllImport(USER32_DLL, EntryPoint="RegisterClassEx")]
public static extern short RegisterClassExA(ref WNDCLASSEX pcWndClassEx);
[DllImport(USER32_DLL, EntryPoint="CreateWindowEx")]
public static extern long CreateWindowExA(long dwExStyle, string lpClassName, string lpWindowName, long dwStyle, long x, long y, long nWidth, long nHeight, long hwndParent, long hMenu, long hInstance, object lpParam);
[DllImport(USER32_DLL, EntryPoint="ShowWindow")]
public static extern int ShowWindow(long hwnd, int nCmdShow);
[DllImport(USER32_DLL, EntryPoint="UpdateWindow")]
public static extern int UpdateWindow(long hwnd);
[DllImport(USER32_DLL, EntryPoint="SetFocus")]
public static extern int SetFocus(long hwnd);
[DllImport(USER32_DLL, EntryPoint="ShowCursor")]
public static extern int ShowCursor(bool bShow);
[DllImport(USER32_DLL, EntryPoint="UnregisterClass")]
public static extern int UnregisterClassA(string lpClassName, long hInstance);
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
WNDCLASSEX wndclassex = new WNDCLASSEX();
wndclassex.cbSize = 48;
wndclassex.style = CS_HREDRAW | CS_VREDRAW;
wndclassex.lpfnWndProc = 0;
wndclassex.cbClsExtra = 0;
wndclassex.cbWndExtra = 0;
wndclassex.hIcon = 0;
wndclassex.hCursor = 0;
wndclassex.hbrBackground = 0;
wndclassex.lpszMenuName = "";
wndclassex.lpszClassName = "myclassname";
wndclassex.hIconSm = 0;
wndclassex.hInstance = 0;
RegisterClassExA(ref wndclassex);
long hWnd = CreateWindowExA(
WS_EX_APPWINDOW,
wndclassex.lpszClassName,
"TAGML",
WS_OVERLAPPED | WS_SYSMENU, // Window won't be resizable
0,
0,
300,
300,
0,
0,
wndclassex.hInstance,
0);
if(hWnd == 0)
{
MessageBox(0, "Unable to create window", ".NET", 0);
UnregisterClassA(wndclassex.lpszClassName, wndclassex.hInstance);
return;
}
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
SetFocus(hWnd);
ShowCursor(true);
//UnregisterClassA(wndclassex.lpszClassName, wndclassex.hInstance);
|
|
|
|
|
Ok at this point I have the callback coming back from the window into C#, but I can't get a valid handle. It keeps coming up as zero. Any idea why the HWND is always zero????
using System;
using System.Runtime.InteropServices;
namespace MyApp
{
public delegate UIntPtr FPtr(IntPtr hWnd, uint uMsg, UIntPtr wParam, UIntPtr lParam);
struct WNDCLASSEX
{
public UInt32 cbSize;
public UInt32 style;
public FPtr lpfnWndProc;
public Int32 cbClsExtra;
public Int32 cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
public string lpszMenuName;
public string lpszClassName;
public IntPtr hIconSm;
}
///
/// Summary description for Class1.
///
class Class1
{
public const string KERNEL32_DLL = "kernel32.dll";
[DllImport(KERNEL32_DLL, EntryPoint="GetModuleHandle")]
static extern int GetModuleHandleA(string lpModuleName);
public const string USER32_DLL = "user32";
public const UInt32 CS_VREDRAW = 0x0001;
public const UInt32 CS_HREDRAW = 0x0002;
public const UInt32 WS_EX_APPWINDOW = 0x00040000;
public const UInt32 WS_OVERLAPPED = 0x00000000;
public const UInt32 WS_SYSMENU = 0x00080000;
public const Int32 SW_SHOWNORMAL = 1;
public const int WM_QUIT = 0x0012;
[DllImport(USER32_DLL, EntryPoint="MessageBox")]
public static extern int MessageBox(int hWnd, String strMessage, String strCaption, uint uiType);
[DllImport(USER32_DLL, EntryPoint="GetForegroundWindow")]
public static extern int GetForegroundWindow();
[DllImport(USER32_DLL, EntryPoint="RegisterClassEx")]
public static extern short RegisterClassExA(ref WNDCLASSEX pcWndClassEx);
[DllImport(USER32_DLL, EntryPoint="CreateWindowEx")]
public static extern IntPtr CreateWindowExA(UInt32 dwExStyle, string lpClassName, string lpWindowName, UInt32 dwStyle, Int32 x, Int32 y, Int32 nWidth, Int32 nHeight, IntPtr hwndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
[DllImport(USER32_DLL, EntryPoint="FindWindow")]
public static extern IntPtr FindWindowA(string lpClassName, string lpWindowName);
[DllImport(USER32_DLL, EntryPoint="FindWindowEx")]
public static extern IntPtr FindWindowExA(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName);
[DllImport(USER32_DLL, EntryPoint="ShowWindow")]
public static extern int ShowWindow(long hwnd, Int32 nCmdShow);
[DllImport(USER32_DLL, EntryPoint="UpdateWindow")]
public static extern int UpdateWindow(long hwnd);
[DllImport(USER32_DLL, EntryPoint="SetFocus")]
public static extern int SetFocus(long hwnd);
[DllImport(USER32_DLL, EntryPoint="ShowCursor")]
public static extern int ShowCursor(bool bShow);
[DllImport(USER32_DLL, EntryPoint="UnregisterClass")]
public static extern int UnregisterClassA(string lpClassName, IntPtr hInstance);
public static UIntPtr WinProc(IntPtr hWnd, uint uMsg, UIntPtr wParam, UIntPtr lParam)
{
Console.WriteLine("WinProc Callback called!!!!!!!");
return UIntPtr.Zero;
}
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
string ClassName = "MyClassName";
string WindowName = "TagML";
WNDCLASSEX wndclassex = new WNDCLASSEX();
wndclassex.cbSize = 48;
wndclassex.style = CS_HREDRAW | CS_VREDRAW;
wndclassex.lpfnWndProc = new FPtr(WinProc);
wndclassex.cbClsExtra = 0;
wndclassex.cbWndExtra = 0;
wndclassex.hInstance = IntPtr.Zero;
wndclassex.hIcon = IntPtr.Zero;
wndclassex.hCursor = IntPtr.Zero;
wndclassex.hbrBackground = IntPtr.Zero;
wndclassex.lpszMenuName = "";
wndclassex.lpszClassName = ClassName;
wndclassex.hIconSm = IntPtr.Zero;
RegisterClassExA(ref wndclassex);
IntPtr hWnd = CreateWindowExA(
WS_EX_APPWINDOW,
wndclassex.lpszClassName,
WindowName,
WS_OVERLAPPED | WS_SYSMENU, // Window won't be resizable
0,
0,
300,
300,
IntPtr.Zero,
IntPtr.Zero,
wndclassex.hInstance,
IntPtr.Zero);
hWnd = FindWindowA(wndclassex.lpszClassName, WindowName);
hWnd = FindWindowExA(IntPtr.Zero, IntPtr.Zero, wndclassex.lpszClassName, WindowName);
if(hWnd == IntPtr.Zero)
{
MessageBox(0, "Unable to create window", ".NET", 0);
UnregisterClassA(wndclassex.lpszClassName, wndclassex.hInstance);
return;
}
|
|
|
|
|
Why isn't CreateWindowEx returning valid window handle?
Easy to check Source Files:
http://www.tagenigma.com/qa/CSharp.Win32.zip
Do I need to setup a hook or something?
http://msdn.microsoft.com/msdnmag/issues/02/10/CuttingEdge/
|
|
|
|
|
Declare a wndProc function . Dont set it to 0;
Ravindra Kumar Sharma
SE ,Monitors
|
|
|
|
|
.Net v2.0 has no need for the InvokeFunc thunk. Instead, the System.Runtime.InteropServices.Marshal class has a GetDelegateForFunctionPointer() method which obtains a delegate for any unmanaged function pointer.
|
|
|
|
|
Okay, So You declare:
<br />
[DllImport("Invoke", CharSet=CharSet.Unicode)]<br />
public extern static int InvokeFunc(int funcptr, int hwnd, <br />
string message, string title, int flags);<br />
So if I want to use Invoke.dll to call a different function WHICH has a different signature - maybe taking two ints and a Guid,I would have to declare the following:
<br />
[DllImport("Invoke", CharSet=CharSet.Unicode)]<br />
public extern static int InvokeFunc(int funcptr, int par1, int par2, Guid par3);<br />
is that correct?
|
|
|
|
|
|
Hello, i need to create a similar Dll to handle the value of the GetProcAddress() function. The value returned by GetProcAddress() is an int value, and i guess i'd need a pointer to this int to be able to call a specific function of an API : WinStationQueryInformationW from WinSta.dll
The problem is i'm bad at C++ so i can't create a dll similar to your Invoke.dll nor understand what you've done in it. :-/
Is it possible to handle the invoking part within a c# class ?
Cause your sample code enables me to use loadlibrary + getProcAddress and make it works with your invokefunc but it's not suitable to call WinStationQueryInformationW.
I'm sorry i'm french and not very experienced so my arguments must be kind of difficult to understand. Anyway, i would be very happy if you can help me.
Thanks
|
|
|
|
|
You're making the (false) assumption that I'm good at C++ - quite the contrary. The Invoke DLL is very very simple assembler - the only x86 assembler I've written...ever.
I'm sure you can make it work.
|
|
|
|
|
I believe you but i don't know the code to fill the dll with, even if it's simple, i have no idea. Is it possible to make this dll with DotNet ?
|
|
|
|
|
it would be very good if you could give me the definition of your Function InvokeFunc() .
|
|
|
|
|
You may be able to do it using Reflection.Emit to create .Net code on the fly and executing that.
The definition for the InvokeFunc is provided in the Zip file - it's called Invoke.asm
|
|
|
|
|
Thanks. So using your invoke function, you think i can call any function stored in any DLL ?
Here is the code i use :
<br />
[DllImport("Invoke", CharSet=CharSet.Unicode)]<br />
public extern static int InvokeFunc(int funcptr, int hwnd, string message, string title, int flags);<br />
<br />
[DllImport("Invoke", CharSet=CharSet.Unicode)]<br />
public extern static int InvokeFunc(int funcptr, HANDLE hServer, long SessionID, WINSTATIONINFOCLASS WinStationInformation, ref WINSTATIONINFORMATION WinInfo,long LenWinInfo,ref long endWinInfo);<br />
Do you think i can use this kind of code or is it totally inapropriate ?
|
|
|
|
|
That looks fine to me. You obviously need to define your HANDLE, WINSTATIONINFOCLASS etc in C# structs - but it should be work fine.
|
|
|
|
|
Hello,
i might be very confusing as i am kindly new to these type of problems in C# language, like bindings, marshalling assemblies and so on.
I wonder why the Microsoft guys are so stupid. I have searched for a week how to solve my problem, and i found no valuable answer till now.
Let me detail a little the problem.
I have a Windows Service App, which makes use of an API Application given by a third party. My application will redistribute this 3rd party app, via a setup package (msi).
In my Service, i use an extern method from the 3rd party main dll.
The problem relies that, even i changed the redistribution path to Program Files Common Files folder, our clients are multinational and these folders are not fix. My problem is how to avoid the dumb DLLImport which cannot take an input param, even that i put Environment.GetCommonFilesFolder
MSDN Site says that if i do a LoadLibrary before the DLLImport, i solved this. So i called the LoadLibrary with variable path in the service constructor.
The problem is that LoadLibrary always return IntPtr.Zero. I tried with LoadLibraryEx, and this worked for passing third param 1 and 2, but it still doesn't solve my problem. I also let the DLLImport with x.dll as first param, without the full path. I get just "The service has been terminated unexpectedly" error, even i catch all exceptions, COMExceptions. There is no catch block executed
Then i moved on downloading this piece of code. My VS .NET 2005 with .NET 2.0 gives errors on assembler file.
Then i found other solution. I removed the DLLImport, and i tried the sequence:
LoadLibraryEx
GetProcAddress
creating a delegate with the same name as my DLL method, and calling this delegate, instead of calling the DLL method. Bullshit, the same error, "Service terminated unexpectedly. It has done this (1) times". Here if i haven't specified 2 as third param to LoadLibraryEx, the GetProcAddress always returns Intptr.Zero.
Knowing that i come from a full managed world, because my experience is totally in Java, and now i am learning C#, i am totally confused about this problem. I cannot imagine the Microsoft guys so dumb not to take into account this possible workflow, because i found nothing related to my problem on the net.
Thank you in advance for any possible suggestion or tutorial,
c y
|
|
|
|
|
Richard,
Nice article!
Another .NET/DLL related question.
How do I implement/simulate DllMain in C#?
As you can imagine the most intriguing part of this question would be the DLL_PROCESS_DETACH event -- Say I want to do some DLL (sorry, assembly) specific cleanup. (Doesn’t it sound similar to the destructor related “issues”?
Thanks in advance.
Alex
|
|
|
|
|
If you want to do something when your app starts - consider adding a static constructor to a class - there is no way to hook DllMain in C#. In MC++, I believe you can hook DllMain, although this was troublesome in v1.0 and partially fixed (IIRC) in v1.1. Nick Hodapp wrote an article about this - see http://www.codeproject.com/scrapbook/semicolon_2.asp
As for an assembly level destructor - this has been talked about by the CLR team at Microsoft and may well have been added for Whidbey.
|
|
|
|
|
Thanks for the article,
Please provide guidelines to make the EXE using VS.Net...
How to link the .asm and .obj files in the IDE?
Thx,
UK
|
|
|
|
|
Hi,
I don't believe it's possible to generate and link assembler in the VS.Net IDE.
Richard
|
|
|
|
|
Hey Richard,
Thx for your help! BTW I also like Curry...
How to build the invoke.dll? just NMAE makefile? is that all?
Cheers,
Uriel
|
|
|
|
|
I downloaded the source code that has been posted here and tried to compile and run it as-is with Visual Studio .NET. However, I get a System.DllNotFoundException on the following line in the .cs file:
int result=InvokeFunc(funcaddr, 0, "Hello World", ".Net dynamic export invocation", 1 /*MB_OKCANCEL*/);
Why doesn't it like this? "DllNotFoundException" suggests to me that for some reason it's not finding User32.dll; however, this file is on my system. Any suggestions?
|
|
|
|
|
<br />
That's a very odd message. You could install DependencyWalker or FileMon to see what's happening.<br />
<br />
I originally wrote the code on Windows 2000 with VS.Net 2002 (beta2 and then RTM). I've just downloaded the code from CodeProject and compiled and run it under Windows XP with VS.Net 2003. Everything works perfectly. Here is the Console output:<br />
<br />
---<br />
<br />
Setting environment for using Microsoft Visual Studio .NET 2003 tools.<br />
(If you have another version of Visual Studio or Visual C++ installed and wish<br />
to use its tools from the command line, run vcvars32.bat for that version.)<br />
<br />
C:\>cd DynInvok_src<br />
<br />
C:\DynInvok_src>cd article_src<br />
<br />
C:\DynInvok_src\article_src>dir<br />
Volume in drive C is SYSROOT<br />
Volume Serial Number is 209C-1E00<br />
<br />
Directory of C:\DynInvok_src\article_src<br />
<br />
01/05/2003 11:56 <DIR> .<br />
01/05/2003 11:56 <DIR> ..<br />
13/11/2001 10:39 904 Invoke.asm<br />
13/11/2001 12:50 548 makefile<br />
13/11/2001 12:49 1,138 UnitTest.cs<br />
3 File(s) 2,590 bytes<br />
2 Dir(s) 488,284,160 bytes free<br />
<br />
C:\DynInvok_src\article_src>nmake<br />
<br />
Microsoft (R) Program Maintenance Utility Version 7.10.2292<br />
Copyright (C) Microsoft Corporation. All rights reserved.<br />
<br />
ml /c /coff /Cp /Fl /Sc /Sg Invoke.asm<br />
Microsoft (R) Macro Assembler Version 7.10.2292<br />
Copyright (C) Microsoft Corporation. All rights reserved.<br />
<br />
Assembling: Invoke.asm<br />
link Invoke.obj -DLL -entry:DllMain /machine:i386 /subsystem:windows /ou<br />
t:Invoke.dll /export:InvokeFunc<br />
Microsoft (R) Incremental Linker Version 7.10.2292<br />
Copyright (C) Microsoft Corporation. All rights reserved.<br />
<br />
Creating library Invoke.lib and object Invoke.exp<br />
csc UnitTest.cs<br />
Microsoft (R) Visual C# .NET Compiler version 7.10.2292.4<br />
for Microsoft (R) .NET Framework version 1.1.4322<br />
Copyright (C) Microsoft Corporation 2001-2002. All rights reserved.<br />
<br />
<br />
C:\DynInvok_src\article_src>dir<br />
Volume in drive C is SYSROOT<br />
Volume Serial Number is 209C-1E00<br />
<br />
Directory of C:\DynInvok_src\article_src<br />
<br />
01/05/2003 11:57 <DIR> .<br />
01/05/2003 11:57 <DIR> ..<br />
13/11/2001 10:39 904 Invoke.asm<br />
01/05/2003 11:56 2,560 Invoke.dll<br />
01/05/2003 11:56 566 Invoke.exp<br />
01/05/2003 11:56 1,712 Invoke.lib<br />
01/05/2003 11:56 3,024 Invoke.lst<br />
01/05/2003 11:56 530 Invoke.obj<br />
13/11/2001 12:50 548 makefile<br />
13/11/2001 12:49 1,138 UnitTest.cs<br />
01/05/2003 11:57 3,584 UnitTest.exe<br />
9 File(s) 14,566 bytes<br />
2 Dir(s) 488,210,432 bytes free<br />
<br />
C:\DynInvok_src\article_src>unittest<br />
Result of invocation is 1<br />
Press any key to continue...<br />
<br />
<br />
C:\DynInvok_src\article_src> <br />
|
|
|
|
|
http://www.msjogren.net/dotnet/eng/samples/dotnet_dynpinvoke.asp
and
http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=7a1fc856-b9ec-41b6-921a-eee7555844de
|
|
|
|
|
Richard Birkby wrote:
Other solutions to the same problem
You probably mean other articles about the same useless trick.
The real issue about P/Invoke is not taking a C/C++ function pointer instead of declaring as many DllImport prototypes you need. The real issue about P/Invoke is the marshaling of parameters.
Unfortunately, none of these articles tackle it.
PS : that said, articles about what I would call "internal implementation of a critical portion of the CLR" is good to have, of course. But just as much as the actual native call (rotor, ecall.c).
|
|
|
|
|
You probably mean other articles about the same useless trick
I wrote my article because of a real-world project needing a technique which is not possible through the standard capabilities of .Net PInvoke. I would expect the other articles were also written for the same reason.
I therefore don't consider these to be "useless tricks".
I look forward to many articles from you discussing alternative parameter marshaling (aka Do-It-Yourself Marshaling, pg 248, Nathan's PInvoke Interop book). Do-It-Yourself Marshaling is a well-documented technique, whereas my article discusses a technique not even covered in Nathan's 1600 page tome.
Richard
|
|
|
|
|