|
You might want to mention in which event your checking for the return key. This should actually be processed in the control's ProcessDialogKey method - which fires before the rich textbox recieves it. This means you have to extend the RichTextBox class and override that method. Don't forget to read the documentation about it, however, instead of just using it. There's things you need to understand about it, including when you override a control method you typically call it on the base class at some point (especially if it's not a message, character, whatever that you want to handle - the base class may need to process it).
As far as scrolling to the bottom, this is actually quite easy but you need to P/Invoke SendMessage and SetScrollPos , which are documented in MSDN[^]. You first send WM_VSCROLL (0x0115) using SendMessage to the other RichTextBox , using it's Handle property (the HWND for that window), along with the scroll information as defined in the WM_VSCROLL documentation. You may also need to call SetScrollPos to set the scroll position for the RichTextBox .
One final approach is to use ScrollToCaret , a public method defined by TextBoxBase (the base class for TextBox and RichTextBox ). Several people have had problems getting this to work and have had to use the approach above; as such, this has been covered here in the forums in the past, so if you'd like to read more about these threads, click "Search comments" immediately above the message board and try searching (I recommend using the keyword "WM_VSCROLL").
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
> You might want to mention in which event your checking for the return key.
I'm using the KeyDown event.
Overriding ProcessDialogKey is something I will research into. However P/Invoking and more advanced things like that are pretty new to me, so it'll take some learning about the basics before I'll be able to do that.
Thanks!
|
|
|
|
|
Hi all~
I would like to ask
I have written a dll by MFC
I would like to written a C# library as interface to use this MFC dll
How to achieve this result?
Is there anything need to change in the MFC code?
Thanks
|
|
|
|
|
Either go through all the problem of adding class factories to your unmanaged MFC DLL in order to use the CallingConvention.ThisCall calling convention with the DllImportAttribute (see a previous post here[^] for the gory details), or - much more easily - write an assembly using Managed C++. In a mixed-mode MC++ assembly, you still have access to all the "unmanaged" C/C++ APIs, including your MFC classes. Wrap those in managed (__gc ) classes and compile your MC++ DLL with the /clr switch. This will produce an assembly that can be used by other managed projects, since all managed code compiles down to Intermediate Language (IL).
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Thanks~
I would like one more question
If the arguement type of a method in MFC dll is "LPCTSTR"
how do I specify the method in C# using this way?
#region DLLImports
[DllImport("test.dll")]
private static extern Set(?? str); // ?? is the "LPCTSTR" in MFC dll
#endregion
Thanks
|
|
|
|
|
It's a string , but it's a platform-dependent string; ANSI in Windows, and Unicode in Windows NT.
So change the DllImportAttribute like so:
[DllImport("test.dll", CharSet=CharSet.Auto)]
private static extern dont-forget-the-return-value Set(string str); Also, unless this is an exported function, what you have won't work. Methods of classes defined in MFC must use the ThisCall CallingConvention so that the first argument (the class pointer) is pushed onto the stack so that the method is executed on that pointer.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
HI~
if the return type is "LSCTSTR"
then is the return type is "string" in C# dll?
but it throws "NotSupportException"
Thanks~
|
|
|
|
|
Yes, but you must make sure that 1) CharSet.Auto is set in the DllImportAttribute and that 2) you use CallingConvention.ThisCall for methods (functions defined on a class). There's many reasons a NotSupportedException could be thrown, so you're just going to have to read those links I gave you before to articles about P/Invoke.
At least having a basic understanding is important to solving your problem.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
|
HI Heath Stewart ~
I have one more question ^.^
If I have implement a MFC dll which is used to show a string on the screen. The dll contain a variable which is stored a string.
Then I write a C# program to invoke the "set" method to set the string variable for the dll.
After a run the C# program, the variable has been updated. However, the string on the screen does not change. I know the dll needs to be refreshed. How to do this?
OnPaint() or using SendMessage() may be used to refresh the screen. But how?
Thank you for answering the questions!
|
|
|
|
|
The C# code shouldn't refresh the screen - the MFC implementation should handle that, which means instead of setting a variable you should call a method (like SetText ); the method implementation should set the variable and invalidate the client region. It's a matter of encapsulation, and this is a much better class design - the class itself should take care of what it manages.
In order to repaint the client region, all you need to do is invalidate it. CWnd::Invalidate would be a logical choice.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
yes, I just use C# program call the method inside the dll.
The method in the dll is set the value of a member variable.
The dll in fact is a screen plugin. The member variable may be a string that is to be shown on the screen.
I would like to change the member variable so that the screen will also be changed to achieve showing a message dynamically.
I can change the member variable but the screen would be change. How can I solve this?
Is the CWnd::Invalidate already existed in the CWnd class?
Or it is needed to be implemented and how to implement it?
|
|
|
|
|
As I said before, if the MFC class encapsulates the string variable, then it should also encapsulate the functionality to update the screen.
If you want to know more about CWnd , look it up on MSDN[^] or ask in the VC++ forum.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Anyone have any ideas on how to access the stored security accounts windows uses for mapping drives automatically ? I'm writting a network drive manager form and I would like to scan the system whent he app starts and store all the shared network drive information, including the username and password used to establish each network drive connection. I've looked at the Network Management API, particularly the NetUseGetInfo but wasn't sure if this was the wrong way to go about it. Any thoughts ?
|
|
|
|
|
You won't be able to get the password, unless storing plain-text passwords is enabled for the domain (and that's the dumbest thing in the world to do unless you have to support client machine 2 decades old or something).
This information isn't exposed in managed API, so you will have to P/Invoke some native API. NetUseGetInfo is as good an API as any - it's what I would've picked having had a lot of experience with the Network Management APIs.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
I'm triing to find in the documentqation what dll the NetGetUseInfo is in do you know ?
|
|
|
|
|
|
Hey Heath,
Thanks again for all your help thus far. I've setup the P/Invoke for NetApi32.dll, but I keep getting the same error so I must have done something wrong....
[DllImport("NetApi32.dll")] private static extern int NetUseGetInfo(string SrvrName,string MappedDrive,int Level,ref structUSE_INFO_2 MapInfo);
[DllImport("NetApi32.dll")] private static extern int NetApiBufferFree(ref structUSE_INFO_2 MapInfo);
[StructLayout(LayoutKind.Sequential)]
private struct structUSE_INFO_2
{
public string sLocal;
public string sRemote;
public string sPassword;
public int iType;
public int iRefCount;
public int iUseCount;
public string sUsername;
public string sDomain;
}
...
....
...
static public string GetMappedDriveUsername(string DriveLetter)
{
string Username=null;
try
{
structUSE_INFO_2 MappInfo = new structUSE_INFO_2();
int Err=NetUseGetInfo(null,DriveLetter.Replace(":",""),2,ref MappInfo);
Username=MappInfo.sUsername;
NetApiBufferFree(ref MappInfo);
}
catch(Exception Err)
{
throw new Exception("GetMappedDriveUsername Error: "+Err.Message);
}
return Username;
}
The error I'm getting is 2250 (This network connection does not exist. ).
I think I'm not seending the mapped drive name correctly, I've tried just the letter, letter with colon, drive description...You said you have alot of experience with these API so I thought you would be able to tell me the NetUseGetInfo 's UseName parameter format.
|
|
|
|
|
You must use CharSet=CharSet.Unicode in your DllImportAttribute , since the strings are declared as LPWSTR (Unicode). The default for the C# and VB.NET compilers is CharSet.Ansi . This makes a big difference. Do use X: as a drive letter (including the colon).
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Oh, and don't forget to set the CharSet field on the StructLayoutAttribute as well.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
ok, now I get an return code of 0, which I'm assuming is the NErr_Success code, with the following code.
[DllImport("NetApi32.dll",CharSet=CharSet.Unicode)] private static extern int NetUseGetInfo(string SrvrName,string MappedDrive,int Level,ref structUSE_INFO_2 MapInfo);
[DllImport("NetApi32.dll",CharSet=CharSet.Unicode)] private static extern int NetApiBufferFree(ref structUSE_INFO_2 MapInfo);
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
private struct structUSE_INFO_2
{
public string sLocal;
public string sRemote;
public string sPassword;
public int iType;
public int iRefCount;
public int iUseCount;
public string sUsername;
public string sDomain;
}
...
...
...
structUSE_INFO_2 MappInfo = new structUSE_INFO_2();
int Err=NetUseGetInfo(null,DriveLetter,2,ref MappInfo);
Username=MappInfo.sUsername;
Err=NetApiBufferFree(ref MappInfo);
However the when I interrogate the structUSE_INFO_2 structure, all the memebers are null . Do I need to set any of the USE_INFO_2 members to a value first ? I didn't see any documentation on it.
|
|
|
|
|
Because the last param is NetUseGetInfo is LPBYTE* , which is a typedef for unsigned char ** . If it was just LPBYTE , your little shortcut of using ref structUSE_INFO_2 would work.
Instead, you have to pass an IntPtr (by the way, it should just be an out param, not a ref , though it doesn't hurt in this case) then use Marshal.PtrToStructure like so:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
internal class GetInfo
{
static void Main(string[] args)
{
if (args.Length != 1)
{
Console.Error.WriteLine("You must specify a drive letter.");
Environment.Exit(1);
}
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
{
Console.Error.WriteLine("Supported only on Windows NT.");
Environment.Exit(2);
}
IntPtr ptr = IntPtr.Zero;
if (0 == NetUseGetInfo(null, args[0], 2, out ptr))
{
UseInfo2 info = (UseInfo2)Marshal.PtrToStructure(ptr,
typeof(UseInfo2));
Console.WriteLine("Username: " + info.username);
Console.WriteLine("Domain: " + info.domainname);
NetApiBufferFree(ptr);
}
else
{
int error = Marshal.GetLastWin32Error();
Console.Error.WriteLine("Error " + error);
Environment.Exit(error);
}
}
[DllImport("netapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern uint NetUseGetInfo
(
string server,
string drive,
uint level,
out IntPtr info
);
[DllImport("netapi32.dll")]
public static extern uint NetApiBufferFree(IntPtr ptr);
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
internal struct UseInfo2
{
public string local;
public string remote;
public string password;
public uint status;
public uint type;
public uint refcount;
public uint usecount;
public string username;
public string domainname;
}
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Awesome, thanks again Heath....
|
|
|
|
|
i want to write my own listview control that i can repaint the columnhead's color.
but i dont know how to do it.
i use Graphics.FillRectangle() in the OnPaint() method but did not get the correct result.
|
|
|
|
|
The ListView class encapsulates the List-View common control. Sticking to pure managed code won't accomplish what you need. You must extend the ListView control, override WndProc , and handle windows messages like you would in VC++.
There are plenty of articles here on CodeProject about this, and you should familiarize yourself with the windows messages and common controls by reading Windows Controls[^].
For one example, see a previous post about defining an event to fire when column widths change: http://www.codeproject.com/script/comments/forums.asp?msg=807702&forumid=1649#xx807702xx[^].
Another good article about handling notification messages for the List-View common control (because that's really what this all comes down to) is http://www.codeproject.com/cs/miscctrl/listviewfilter.asp[^].
In your case, since you're paining, you'll need to handle the NM_CUSTOMDRAW message (for a list-view). This will, once you've handle the right sequence of windows messages, give you an HDC (handle to a device context). You can get a Graphics object from this by using Graphics.FromHdc , but simply overriding the OnPaint method (or handlig the Paint event, which is much slower and a bad idea when done in a derived control) will not work. You must draw to the DC that the list-view is using for the list-view column header.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|