|
-
Jan 24th, 2007, 10:08 PM
#1
Thread Starter
Addicted Member
EditStreamCallback() and outside apps
I have seen plenty of VB6 examples of EditStreamCallback using EM_STREAMIN/OUT. Here's one from this site:
http://vbforums.com/showthread.php?t=336077
However, I've yet to see one for .NET anywhere and I really need help setting it up. If I could get some help I would greatly appreciate it, please use Notepad as the example external app.
I just need to get text and set text in a form on an external app and WM_GET/SETTEXT don't work because it's a Rich Text form... I am already familiar with basic API functions like FindWindow, SendMessage, and PostMessage, but I'm clueless when it comes to EditStreamCallback... Please start from square one if possible.
-
Jan 25th, 2007, 07:24 PM
#2
Frenzied Member
Re: EditStreamCallback() and outside apps
Try this:
VB Code:
Const EM_STREAMOUT = 1098
Const SF_RTF = 2
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByRef es As EditStream) As Integer
Public Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByRef es As EditStream) As Integer
Public Structure EditStream
Public dwCookie As Integer
Public dwError As Integer
Public lpEditStreamCallBack As EditStreamCallBackType
End Structure
Public Delegate Function EditStreamCallBackType(ByVal dwCookie As Integer, ByVal buffer As String, ByVal numBytes As Integer, ByVal lpCb As Integer) As Integer
Public Function myEditStreamCallBack(ByVal dwCookie As Integer, ByVal buffer As String, ByVal numBytes As Integer, ByVal lpCb As Integer) As Integer
'your code here
'when thread goes through here, do what you want with the data, such as the buffer
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim es As EditStream
es = New EditStream
es.dwCookie = 0
es.dwError = 0
es.lpEditStreamCallBack = AddressOf myEditStreamCallBack
Dim ret As Integer
ret = SendMessage(RichTextBox1.Handle.ToInt32, EM_STREAMOUT, SF_RTF, es)
End Sub
Last edited by benmartin101; Jan 25th, 2007 at 07:27 PM.
-
Jan 25th, 2007, 07:38 PM
#3
Thread Starter
Addicted Member
Re: EditStreamCallback() and outside apps
Thanks for the help but...
What exactly is this code doing? can you comment it a little bit please? If you feel like it, would you put an example of using this like for getting/sending text to and from wordpad or something?
-
Jan 26th, 2007, 12:00 PM
#4
Frenzied Member
Re: EditStreamCallback() and outside apps
You could copy the code to a new vb.net project. Add a richtextbox control, named RichTextBox1. So after the SendMessage() is called, if you place a breakpoint within myEditStreamCallBack(), then check the value of the parameter "buffer", you'll see that its the data in the richtextbox.
-
Jan 26th, 2007, 05:26 PM
#5
Thread Starter
Addicted Member
Re: EditStreamCallback() and outside apps
I really appreciate the help but I just don't really understand all of this you know?
When my program executes the SendMessage function and uses the editstream, it never executes the code within myEditStreamCallback... I can't even get a message box to pop up... What could the problem be?
And could you please go more in depth to this? I really don't understand any of this callback function and I would like to for future use. Please make an example that shows you using the edit stream to grab what is written in an outside app's rich text box(wordpad for ex)... I would also like to see how to set the text in that rtb from my program as well.
-
Jan 27th, 2007, 09:40 PM
#6
Thread Starter
Addicted Member
Re: EditStreamCallback() and outside apps
Another problem I'm having is when I send the EM_STREAMOUT message to a rich text form, it always crashes the program I'm sending it to.
-
Jan 29th, 2007, 04:27 PM
#7
Frenzied Member
Re: EditStreamCallback() and outside apps
A callback function, to my understanding, is basically an application-defined function. When you run an application, it has its own EditStreamCallBack function, which it calls everytime EM_STREAMOUT or EM_STREAMIN is sent. What we are trying to do is make it call our own "custom" EditStreamCallBack function. So instead of it going to its usual callback function, it goes to ours, which in turn, allows us to do stuff with its parameters; whether to use them or change them. That's why in my previous post when I said to put a breakpoint in the "custom" EditStreamCallBack function, it will stop there during debug because its now using that function as its callback.
However, my previous post will not work with external(outside) apps. To do it with external apps, you will need to "inject" code into the app. To be more precise, you will need to inject the "custom" callback function code. The reason for this is because when you send EM_STREAMOUT or EM_STREAMIN to the external application, it will look for the function it its OWN (the external app's) process memory. Remember the EditStream structure? Its properties include the pointer (or memory address) to the "custom" callback function, but that function only exist in your main application's process memory, not in the external app. When the external app looks into that memory location(within its own process memory), it crashes because the "custom" function does not exist there. So anyways, here is the code for the dll, which needs to be done using C++.
Continued on the next post...
-
Jan 29th, 2007, 04:28 PM
#8
Frenzied Member
Re: EditStreamCallback() and outside apps
Code:
#include "stdafx.h"
#pragma data_seg(".shared")
char sharedStr[255] = "a";
char sharedRichTextBoxString[255] = "a";
int addr = 0;
HMODULE module = 0;
int flag = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.shared,RWS")
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if(flag==0)
{
module = GetModuleHandle("C:\\Program Files\\Microsoft Visual Studio\\MyProjects\\EditStreamCallBack\\Debug\\EditStreamCallBack.dll");
addr = (int)GetProcAddress(module, (LPCSTR)"EditStreamCallBack");
flag = 1;
}
return TRUE;
}
__declspec(dllexport) void _stdcall SetText(char * str)
{
int sz = strlen(str);
memcpy(sharedStr, str, sz);
}
__declspec(dllexport) HMODULE _stdcall GetModuleH()
{
return module;
}
__declspec(dllexport) int _stdcall GetAddr()
{
return addr;
}
__declspec(dllexport) char * _stdcall GetRichTextBoxString()
{
return sharedRichTextBoxString;
}
__declspec(dllexport) int CALLBACK EditStreamCallBack(DWORD dwCookie, LPBYTE buf, DWORD numByte, DWORD* cb)
{
if(dwCookie==1) //if 1, then we want it to work for EM_STREAMIN
{
memcpy(buf, sharedStr, strlen(sharedStr));
*cb = strlen(sharedStr);
}
else if(dwCookie==2) //if 2, then we want it to work for EM_STREAMOUT
{
memcpy(sharedRichTextBoxString, buf, numByte);
}
return 0;
}
When you've compiled that to a .DLL, here's the .NET code for the main application
VB Code:
Const EM_STREAMOUT = 1098
Const SF_RTF = 2
Const SF_TEXT = 1
Const EM_STREAMIN = 1097
Const SF_UNICODE = 16
Const SFF_PLAINRTF = &H4000
Const MEM_COMMIT = &H1000
Const PAGE_READWRITE = &H4
Public Const PROCESS_ALL_ACCESS As Integer = &H1F0FFF
Public Const PROCESS_VM_WRITE As Integer = &H20
Const INFINITE = &HFFFFFFFF
Public Declare Function WaitForSingleObject Lib "kernel32" Alias "WaitForSingleObject" (ByVal hHandle As Integer, ByVal dwMilliseconds As Integer) As Integer
Public Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Integer, ByRef lpdwProcessId As Integer) As Integer
Public Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As IntPtr, ByVal lpBaseAddress As Integer, ByRef lpBuffer As Byte, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Integer
Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Integer, ByVal lpBaseAddress As Integer, ByVal lpBuffer As String, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Integer
Declare Function WriteProcessMemory_EditStream Lib "kernel32" Alias "WriteProcessMemory" (ByVal hProcess As Integer, ByVal lpBaseAddress As Integer, ByRef lpBuffer As EditStream, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Integer
Public Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Integer, ByVal bInheritHandle As Integer, ByVal dwProcessId As Integer) As Integer
Public Declare Function VirtualAllocEx Lib "kernel32" Alias "VirtualAllocEx" (ByVal hproc As Integer, ByVal lpAddress As Integer, ByVal dwSize As Integer, ByVal flAllocationType As Integer, ByVal flProtect As Integer) As Integer
Public Declare Function CreateRemoteThread Lib "kernel32" Alias "CreateRemoteThread" (ByVal hProcess As Integer, ByVal lpThreadAttributes As Integer, ByVal dwStackSize As Integer, ByVal lpStartAddress As Integer, ByVal lpParameter As Integer, ByVal dwCreationFlags As Integer, ByVal lpThreadId As Integer) As Integer
Public Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Integer
Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal filename As String) As Integer
Declare Function GetProcAddress Lib "kernel32" Alias "GetProcAddress" (ByVal hmod As Integer, ByVal funcName As String) As Integer
'change dll path wherever you put the DLL file
Declare Sub SetText Lib "C:\Program Files\Microsoft Visual Studio\MyProjects\EditStreamCallBack\Debug\EditStreamCallBack.dll" (ByVal str As String)
Declare Function GetAddr Lib "C:\Program Files\Microsoft Visual Studio\MyProjects\EditStreamCallBack\Debug\EditStreamCallBack.dll" () As Integer
Declare Function GetRichTextBoxString Lib "C:\Program Files\Microsoft Visual Studio\MyProjects\EditStreamCallBack\Debug\EditStreamCallBack.dll" () As String
Declare Function GetModuleH Lib "C:\Program Files\Microsoft Visual Studio\MyProjects\EditStreamCallBack\Debug\EditStreamCallBack.dll" () As Integer
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lpEditStream As Integer) As Integer
Public Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByRef es As EditStream) As Integer
Public Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByVal dest As IntPtr, ByVal source As String, ByVal length As Integer)
Dim hwnd As Integer = &H3B04E2 'handle of the richedit textbox
Dim hproc As Integer
Dim esAddr As Integer
Public Structure EditStream
Public dwCookie As Integer
Public dwError As Integer
Public lpEditStreamCallBack As Integer
End Structure
Public Delegate Function EditStreamCallBackType(ByVal dwCookie As Integer, ByVal buffer As IntPtr, ByVal numBytes As Integer, ByVal cb As IntPtr) As Integer
'injects dll code into (external)target process
Private Sub InjectCode()
Dim pid As Integer
'path to EditStreamCallBack.dll - may vary depending on where you put your dll
Dim path As String = "C:\Program Files\Microsoft Visual Studio\MyProjects\EditStreamCallBack\Debug\EditStreamCallBack.dll"
GetWindowThreadProcessId(hwnd, pid)
hproc = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
Dim remoteAddr As Integer = VirtualAllocEx(hproc, 0, Len(path), MEM_COMMIT, PAGE_READWRITE)
Dim ret As Integer = WriteProcessMemory(hproc, remoteAddr, path, Len(path), 0)
Dim hmod As Integer = GetModuleHandle("kernel32")
Dim address = GetProcAddress(hmod, "LoadLibraryA")
Dim ret2 As Integer = CreateRemoteThread(hproc, 0, 0, address, remoteAddr, 0, 0)
WaitForSingleObject(ret2, INFINITE)
End Sub
Private Sub Setup()
Dim es As EditStream
InjectCode()
'we are getting the address of the dll function "EditStreamCallBack"
Dim extAddress As Integer = GetAddr
'string we want to write into richtextbox, which includes the RTF keycodes.
'As a final result, it should display "Cplusplus work" in the richtextbox if it works correctly
SetText("{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fswiss\fcharset0 Arial;}}{\colortbl ;\red128\green0\blue0;}{\*\generator Msftedit 5.41.21.2506;}\viewkind4\uc1\pard\cf1\f0\fs20 Cplusplus work\cf0\par}")
es = New EditStream
es.dwCookie = 1 'use 1 if sending EM_STREAMIN or 2 if sending EM_STREAMOUT; reason: thats how our callback function is set up
es.dwError = 0
es.lpEditStreamCallBack = extAddress
'allocate memory for EditStream structure in (external)target process
esAddr = VirtualAllocEx(hproc, 0, Marshal.SizeOf(es), MEM_COMMIT, PAGE_READWRITE)
'write EditStream structure into (external)target process
'reason: When sending EM_STREAMIN to an external app, the external app looks for the EditStream object in its own process.
Dim res As Integer = WriteProcessMemory_EditStream(hproc, esAddr, es, Marshal.SizeOf(es), 0)
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Setup()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'send EM_STREAMIN
SendMessage(hwnd, EM_STREAMIN, SF_RTF, esAddr)
End Sub
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
'send EM_STREAMOUT
SendMessage(hwnd, EM_STREAMOUT, SF_RTF, esAddr)
Dim val As String = GetRichTextBoxString() 'gets the RTF string, which includes the RTF keycodes, in the richtextbox
End Sub
End Class
You will want to first call Setup() which basically injects the DLL containing the "custom" EditStreamCallBack function. Why does it have to be in a DLL? Not really sure about the specific reason, but it has to be in these cases. If you want to learn about the inject() function, go to msdn and read about what those functions, that were used, are doing. Then either call SendMessage(...EM_STREAMOUT) or SendMessage(...EM_STREAMIN). Note: You have to change dwCookie (of EditStream structure) depending on which message your sending.
Last edited by benmartin101; Jan 29th, 2007 at 04:48 PM.
-
Jan 29th, 2007, 04:46 PM
#9
Thread Starter
Addicted Member
Re: EditStreamCallback() and outside apps
Ok thanks. This will take some testing to see if it works for what I'm doing but it looks good.
Is there a way to package that .dll directly in the .exe rather than having two files?
-
Jan 29th, 2007, 04:52 PM
#10
Frenzied Member
Re: EditStreamCallback() and outside apps
I don't think there is a way to package the dll into the exe. At least none that i know of.
-
Jan 29th, 2007, 05:10 PM
#11
Addicted Member
Re: EditStreamCallback() and outside apps
i just wonder if you guys have an example of VB6 instead of VB.NET. In the C++ dll code, is there anyway to change the absolute path of the dll.
Thanks guys
-
Jan 29th, 2007, 05:48 PM
#12
Frenzied Member
Re: EditStreamCallback() and outside apps
For the dll, yeah...you could change it. You should change the path to wherever the dll is located, or it won't work. For converting to VB6, it should be pretty straightforward. Some changes are needed. For the declarations of the dll functions that are being imported, just change all INTEGER types to LONG. I think the declaration for STRUCTURE in vb6 is a little different, but i'm gonna assume you know how to do structures in vb6. For the code Marshal.SizeOf(), you could use vb6's Len(). Also in vb6, this code is not allowed:
dim x as integer = 1
should be:
dim x as integer
x = 1
Regarding the DELEGATE code, that is not even needed in this whole project so just take it out. So converting it to vb6 shouldn't be so hard. Just make sure that those dll function declarations have the exact same BYVAL or BYREF keyword as in mine.
-
Feb 5th, 2007, 11:55 AM
#13
Addicted Member
Re: EditStreamCallback() and outside apps
Did anybody try it yet ? i try it and it just hang the external app. so bad
-
Feb 5th, 2007, 03:36 PM
#14
Frenzied Member
Re: EditStreamCallback() and outside apps
could you post your codes? Including the declarations of the dll functions.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|