Komunikacja między aplikacjami - Windows Message WM_COPY - jak przechwycić?

0

Witam. Mam 2 aplikacje (C++), które komunikują się za pomocą WM_COPY. Podczas komunikacji, pierwsza działa jako aplikacja główna, druga jako serwer. Mam swoją aplikację w C#, którą chcę zastąpić tą drugą serwerową, ale potrzebuję pomocy jak przechwycić w mojej aplikacji message WM_COPY z pierwszej aplikacji?

Takie info dostałem od producenta:

At AXXX-NC startup, a new process of SXXX is created, then NC will send ID_COPYDATA_EXESERVER command with data : XXXDATA1
SXXX will now run in server mode, so it will be hidden and listening to further commands
when user will click "take picture" button on NC, NC will send a ID_COPYDATA_TAKEPHOTO command with data : XXXDATA2
when the picture is ready and saved, SXXX will send back a command ID_COPYDATA_PHOTODONE with data : XXXDATA3
All commands are sent and received using WM_COPY data.

0

Odświeżam. Spy++ pokazuje WM_COPYDATA, a nie WM_COPY.

0
litrmleka napisał(a):

Odświeżam. Spy++ pokazuje WM_COPYDATA, a nie WM_COPY.

Rozumiem, że brak dostępu do źródeł. To jest przesyłanie komunikatów pomiędzy okienkami, więc trzeba zgadnąć jakie okienko wyszukuje jedna i druga aplikacja. Prawdopodobnie używając funkcji FindWindow z WIN API. Poszperaj jaką klasę mają ich okienka i tytuł/nazwę .

0

Dostępu do źródeł brak, ale mam kontakt z producentem, który może odpowiedzieć mi na wszystkie pytania. Wiem jakie okienka w obu aplikacjach. Udało mi się już wysłać, nie wiem tylko jak przechwycić odpowiedź.

0

Przekazywanie wiadomości działa tak, że wysyłający zna okno i śle do niego WM_COPYDATA, a odbierający w funkcji okienkowej ją odbiera w pętli pobierającej wiadomości. W C# też można się podczepić pod komunikaty WIN API. Ale zanim zaczniesz sprawdzać jak, lepiej niech producent wyjaśni Ci konwencję identyfikacji okienek z każdej aplikacji. Tu jest przykład z MSDN jak komunikacja poglądowo może wyglądać: https://msdn.microsoft.com/pl-pl/library/windows/desktop/ms649009(v=vs.85).aspx. Oczywiście to jest w c - naturalnym języku WIN API. Zawsze najpierw trzeba rozumieć protokół komunikacji na wyższym poziomie abstrakcji zanim zaczniesz babrać się w szczegółach.

0

I właśnie chodzi mi o to jak zaimplementować tą pętlę pobierającą wiadomości. Znam wszystkie parametry przekazywane w wiadomości.

0
litrmleka napisał(a):

I właśnie chodzi mi o to jak zaimplementować tą pętlę pobierającą wiadomości. Znam wszystkie parametry przekazywane w wiadomości.

W przechwytywaniu wiadomości okienkowych z c# chodzi z grubsza o to:
https://msdn.microsoft.com/pl-pl/library/system.windows.forms.control.wndproc(v=vs.110).aspx
Musisz podobnie zrobić z WM_COPYDATA, rozpakować wszystko porobić rzutowania do typów w C# i dalej to obrabiać jak chcesz.
Wieczorem podrzucę jak czas pozwoli taki przykład dla WM_COPYDATA

0

Byłbym wdzięczny. Właśnie nie wiem jak w WndProc przekazać uchwyt z jakiej aplikacji (procesu) ma odbierać, lub jak ustawić żeby czytał wszystko globalnie.

1
litrmleka napisał(a):

Byłbym wdzięczny. Właśnie nie wiem jak w WndProc przekazać uchwyt z jakiej aplikacji (procesu) ma odbierać, lub jak ustawić żeby czytał wszystko globalnie.

Troszkę nie do końca rozumiem z czym masz problem. Co to za uchwyt i nie znam protokołu między tymi aplikacjami.
Jeśli chodzi o komunikat WM_COPYDATA, to dostaną go tylko okienka najwyższego poziomu, czyli kontrolki już nie, czyli musisz to w formatce obsługiwać w c#. Ale pewnie to robisz tak. Natomiast z kontrolkami/formatkami w c# jest tak, że za każdą stoi okienko z WIN API. Do jego uchwytu dostajesz się stosując this->Handle. Jakkolwiek IntPtr zawiera 64 bitowy wskaźnik w aplikacjach 64 bitowych to bierzesz tylko dolną połówkę, bo uchwyty są dla wszystkich aplikacji 32 bitowe. Co więcej uchwyty okien są ważne w obrębie systemu a nie tylko aplikacji.

Tak sobie piszę to wszystko, ale faktycznie to nie wiem czy przypadkiem nie masz problemu z protokołem tak na prawdę. Sensowny szkic zasad protokołu dla WM_COPYDATA może wyglądać tak:
serwer rejestruje znaną klasę okna i tworzy ukryte okno ze znanym tytułem, ew. tytułem parametryzowanym jakimiś niewielkimi liczbami całkowitymi (jeśli dopuszcza się istnienie kilku serwerów )
klienci wysyłają do serwera rekordy wiadomości zawierające m.in. hwnd okienka, w którym oczekują na odpowiedź serwera. Jeśli w takim okienku oczekują na odpowiedzi od innych serwerów lub kilka wywołań symultanicznych do serwera to należy rekord rozszerzyć o identyfikator żądania, który będzie jednoznaczną liczbą w obrębie klienta i pozwoli mu rozróżnić, na które wywołanie do serwera dostaje odpowiedź.
Dalej trzymając się tych zasad tworzysz rekordy poszczególnych typów wywołań serwera i odpowiedzi na nie.

Tak jak napisałem prześlę przykład ale muszę chwilkę wolną znaleźć, bo nawet nic do c# nie mam zainstalowanego pod ręką. Nie wiem zresztą czy to coś pomoże w Twoim przypadku. Wolałbym zobaczyć fragment kodu, w którym walczysz z serwerem, czy próbujesz go zaimplementować - już nie pamiętam, którą stronę chciałeś sam obsługiwać.

0

Dzięki za podpowiedzi, trochę to rozwinę, żeby zobrazować z czym mam problem. Aplikacje są 32 bitowe.
Uruchamiam aplikację główną "NC.EXE", która uruchamia w tle aplikację "PHOTO.EXE" - nasz serwer. Jeśli w "NC" zostanie kliknięty button, wysyła message (WM_COPYDATA) do "PHOTO", który robi swoje zadanie i odsyła wiadomość ze ścieżką i parametrami. Ja potrzebuję zastąpić serwer "PHOTO" swoją aplikacją.
Zrobiłem w taki sposób wysyłanie wiadomości do "NC" ze swojej aplikacji, działa bo dostaję komunikat z "NC", w uproszczeniu:

Process process = Process.GetProcessesByName("NC").FirstOrDefault();
hWnd= process.MainWindowHandle;
SendMessage(HwND, WM_COPYDATA, 0, Buffer);

Myślę, że w drugą stronę, czyli odpowiedź od "PHOTO" działa na tej samej zasadzie, czyli identyfikuje po odpalonym procesie. Ewentualnie uchwyt pobierany jest w momencie uruchomienia. Niestety nie mogę odebrać żadnego komunikatu, ponieważ to działa tylko jeśli są "adresowane" do mojej aplikacji:

protected override void WndProc(ref Message m)
{
if (m.Msg == WM_COPYDATA)
{
//
}
base.WndProc(ref m);           
}

Reasumując mój problem - chciałbym przechwycić wszystkie messages WM_COPYDATA z aplikacji "NC".

0

Zbyt mało kodu mi pokazałeś. Nie wiem przykładowo jak przygotowujesz bufor dla WM_COPYDATA. Zauważ, że jak zmienisz nazwę aplikacji nc na inną to komunikacja się rozsypie od razu. Dość ryzykowne jest też mapowanie proces->główne okno aplikacji. Nie wiem czy MS gwarantuje gdziekolwiek, że główne okno aplikacji zawsze będzie związane z formatką winforms. Nawet jeśli jest tak obecnie to w przyszłej implementacji platformy NET może to zostać potencjalnie zmienione.
Ogólniej patrząc masz problem z tym wszystkim o czym pisałem wcześniej tzn głównie ze znajomością protokołu. Czy producent apki go ukrywa? Podejrzewam, że albo w komunikacie do serwera klient podaje hwnd swojego okna, w którym oczekuje odpowiedzi albo traktuje klienta jak drugi serwer czyli wyszukuje jego okna na podstawie ustalonej z góry klasy i tytułu. Ten ostatni wariant sugerowałby, że producent nie jest zbyt refleksyjnym człowiekiem.
Więcej kodu jeśli mam zgadnąć co robisz.

0

Trochę czekałem na odpowiedź od producenta, o więcej szczegółow, oto ona:

  1. we use method that handle a ON_WM_COPYDATA event, where it's passed a COPYDATASTRUCT.
  1. You get no reply from NC because when a picture is passed from photo to NC, Photo returns in "listening mode" to take next picture and so on.

We used FindWindow, but it's not that good because you may get an Explorer window that has NC in its title, so I really suggest to look into current processes.

Odnośnie bufora dla WM_COPYDATA, mój testowy kod:

struct COPYDATASTRUCT
{
public uint dwData;
public int cbData;
public IntPtr lpData;
}

COPYDATASTRUCT cds;

cds.dwData = 0;
cds.cbData = message.Length + 1;
cds.lpData = Marshal.StringToHGlobalAnsi(message);

IntPtr Buffer = IntPtrAlloc(cds);

static IntPtr IntPtrAlloc<T>(T param)
{
IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param));
Marshal.StructureToPtr(param, retval, false);
return (retval);
}
        

Strukturę bufora mam od producenta (message: "THICK=0.,SUBTH=0.,CAMERA=1")

0

To jest komunikat do serwera? Zakładam, że rozumiesz jego semantykę... Czyli ten serwer nic nie opowiada klientowi? Bardzo dziwny ten protokół... Rozumiem, że producent wyszukuje okno aplikacji serwera po wg nazwy procesu, tak? Na marginesie - chyba ten twój producent to nastolatek, który uczy się rzemiosła na swoich klientach.
Mam pytanie - czy masz problem z kodem, który ma wysłać taki komunika? Tutaj masz kod przykładowy w c https://msdn.microsoft.com/pl-pl/library/windows/desktop/ms649009(v=vs.85).aspx Podsyłam żebyś bez zaciemniania widział jaka jest idea WM_COPYDATA. Zobacz, że poprzez rekord myCDS wysyłasz rekord myRec o formacie który sam definiujesz.

Odpowiedź na komentarz:

Wszystkie znaki na niebie i ziemi wskazują, że to string. ( typ argumentu StringToHGlobalAnsi oraz posiadanie właściwości length zgodnej semantycznie z length klasy String ). W dodatku string nie korzystający z unikodu raczej.
Nie powinieneś globalnie przechwytywać WM_COPYDATA, bo się wetniesz w komunikację innych aplikacji między sobą.
Weź jakiś program do debugowania komunikatów okienkowych i podglądania okienek i zobacz jaką nazwę oraz jaką klasę (w sensie win api ) ma okienko serwera, do którego chcesz słać te "message". O ile nazwa tak jak piszą może wchodzić w konflikt, to nazwa klasy już niekoniecznie (o ile głupio jej nie nadali ). A tak na marginesie czemu sam chcesz napisać aplikację kliencką?

0

FindWindow pokazuje mi jaka nazwa i jaka klasa. Ale w C# chyba raczej nie podmienię nazwy na inną niż "WindowsForms10.Window..."? Czy muszę napisać coś do forwardowania tych message'ów w C++ do mojego serwera w C#? Czy jest jakiś inny sposób jeżeli identyfikują też po ClassName?

Chcę podmienić aplikację kliencką, ponieważ mam gotowe lepsze rozwiązanie (i tańsze sprzętowo). Producent o tym wie, i w teorii próbuje mi "pomóc", dalej będę korzystał z głównej aplikacji, chcę podmienić tylko ten serwer.

0
litrmleka napisał(a):

FindWindow pokazuje mi jaka nazwa i jaka klasa. Ale w C# chyba raczej nie podmienię nazwy na inną niż "WindowsForms10.Window..."? Czy muszę napisać coś do forwardowania tych message'ów w C++ do mojego serwera w C#? Czy jest jakiś inny sposób jeżeli identyfikują też po ClassName?

Chcę podmienić aplikację kliencką, ponieważ mam gotowe lepsze rozwiązanie (i tańsze sprzętowo). Producent o tym wie, i w teorii próbuje mi "pomóc", dalej będę korzystał z głównej aplikacji, chcę podmienić tylko ten serwer.

Sorry za opóźnienie spowodowane losowymi wypadkami. Wieczorem pomyślę i odpiszę. Chwilowo mam umysł przestawiony na unix-owy świat. Muszę dokonać resetu. :-)

1
litrmleka napisał(a):

FindWindow pokazuje mi jaka nazwa i jaka klasa. Ale w C# chyba raczej nie podmienię nazwy na inną niż "WindowsForms10.Window..."? Czy muszę napisać coś do forwardowania tych message'ów w C++ do mojego serwera w C#? Czy jest jakiś inny sposób jeżeli identyfikują też po ClassName?

Chcę podmienić aplikację kliencką, ponieważ mam gotowe lepsze rozwiązanie (i tańsze sprzętowo). Producent o tym wie, i w teorii próbuje mi "pomóc", dalej będę korzystał z głównej aplikacji, chcę podmienić tylko ten serwer.

Jeśli chodzi o utworzenie okna z daną nazwą klasy i tytułem ( jak rozumiem też ukrytego ) to możesz to zrobić w dll-ce napisanej w c/c++, która na przykład po odebraniu wiadomości będzie wstawiać przerobioną wiadomość do kolejki komunikatów i takie komunikaty można w C# odbierać. Robienie tego w oddzielnym procesie to raczej byłoby sztuczne rozwiązanie.
Pytanie tylko czy już ustaliłeś wszystko. Czy np faktycznie NC tak wyszukuje server ( tutaj bezpośrednim dowodem byłoby napisanie testowej apki w c++ instalującej okienko a la server i sprawdzenie ze do niej idzie komunikat z NC. Można by ja przerobić w tą dll-kę zresztą jak wszystko zadziała ). Druga kwestia jak server wybiera okienko do wysłania odpowiedzi. Ale tutaj możesz zastosować protezę polegająca na tym, że do okna NC będziesz to wysyłał, a je znajdziesz podobnie na podstawie ustalonych nazw klasy i tytułu okna ( zakładam, że serwer pisali w sposób uniwersalny a nie tylko pd kątem tego NC ).

0

Właśnie tak zrobiłem, napisałem apkę w C++ pośredniczącą między NC a moim serwerem. Działa. Ale czekam jeszcze na odpowiedź od producenta, ponieważ w NC wyświetla mi błąd w składni odebranej wiadomości. Prawdopodobnie jest jakiś błąd albo literówka w tej składni, którą mi podali. Nie mogłem złapać NC za pomocą FindWindow, ale rozwiązałem to tak że w C# przechwytuje ją sobie po procesie i przekazuje HWND do tego forwardera w C++.

0

P.S. Jak w C++ można zrobić coś takiego jak w C#?

Process proc = Process.GetProcessesByName("NC").FirstOrDefault();
hWnd = proc.MainWindowHandle;
1
litrmleka napisał(a):

P.S. Jak w C++ można zrobić coś takiego jak w C#?

Process proc = Process.GetProcessesByName("NC").FirstOrDefault();
hWnd = proc.MainWindowHandle;

Coś ci poniżej skleciłem na szybko, ale nie bezmyślnie. Kod działa u mnie. Pobaw się.
Źródła podałem w komentarzu ale mocno przerobiłem te przykłady, by były użyteczne.
enableDebugPriv jest potrzebne by nadać uprawnienia aplikacji do m.in przeglądania procesów.

// sample code for litrmleka

#include "stdafx.h"

//below should go into header file 

#include <windows.h>

void enableDebugPriv();
DWORD findProcIdByName( TCHAR* pname );
int findProcTopWindows( DWORD process_id, unsigned cond, HWND *hwndVec, int vecSize, int *hasMore );

#define FPW_ALL 3
#define FPW_HIDDEN 1
#define FPW_VISIBLE 2

//end header


#include <string.h>
//#include <cstdio>
#include <windows.h>
#include <tlhelp32.h>


#define MAXWIN 10
HWND hwndVect[MAXWIN];

int _tmain(int argc, _TCHAR* argv[])
{

	//HANDLE hProcess; 
	DWORD pid; int n;

	enableDebugPriv();
	pid = findProcIdByName(_T("putty.exe"));
	if ( !pid ) 
		return -1;

	n = findProcTopWindows(pid, FPW_HIDDEN, hwndVect, MAXWIN, 0 );
	n = findProcTopWindows(pid, FPW_VISIBLE, hwndVect, MAXWIN, 0 );

    return 0;
}


/*	
	
	implementation based derrived from 

	https://stackoverflow.com/questions/1888863/how-to-get-main-window-handle-from-process-id	
	https://stackoverflow.com/questions/865152/how-can-i-get-a-process-handle-by-its-name-in-c

*/

struct search_data {
    DWORD process_id;	
    HWND window_handle;
	unsigned cond;

	HWND *hwnd_vect;
	int	  hwnd_size;
	int	  hwnd_max;
};

int addHwnd( struct search_data *sdat, HWND hwnd ){

	if ( sdat->hwnd_size < sdat->hwnd_max ){
		sdat->hwnd_vect[sdat->hwnd_size++] = hwnd;
	}else{
		sdat->hwnd_max = 0;
		return -1;
	}
	if ( sdat->hwnd_size < sdat->hwnd_max )
		return 1;
	return 0;
}


//BOOL is_main_window(HWND hwnd)
//{   
//	return GetWindow(hwnd, GW_OWNER) == (HWND)0 && IsWindowVisible(hwnd);
//}

BOOL notOwnedWindow(HWND hwnd)
{   
	return GetWindow(hwnd, GW_OWNER) == (HWND)0;
}

BOOL CALLBACK enumWinCb(HWND hwnd, LPARAM lParam)
{
    DWORD process_id = 0;
	int vis,r;
	struct search_data* data = (struct search_data*)lParam;

    GetWindowThreadProcessId(hwnd, &process_id);

    if (data->process_id != process_id || ! notOwnedWindow(hwnd))
        return TRUE; //continue search
	
	vis = IsWindowVisible(hwnd);

	if ( (data->cond & FPW_HIDDEN) && vis )
		return TRUE; 
	if ( (data->cond & FPW_VISIBLE) && !vis )
		return TRUE; 

	r = addHwnd(data,hwnd);

	if ( r >=1 )
		return TRUE;

    //data->window_handle = handle;
    return FALSE;   //stop search
}

int findProcTopWindows( DWORD process_id, unsigned cond, HWND *hwndVec, int vecSize, int *hasMore )
{
    search_data data;

	if ( vecSize <= 0 ) return -1;

    data.process_id = process_id;
	data.cond = cond; 
	data.hwnd_vect = hwndVec;
	data.hwnd_max = vecSize;
	data.hwnd_size = 0;

    //data.window_handle = 0;

    EnumWindows( enumWinCb, (LPARAM)&data );

	if (hasMore){
		if (data.hwnd_max==0)	*hasMore = 1;
		else *hasMore = 0;				
	}

	return data.hwnd_size;
}

#if 0

HWND find_main_window(DWORD process_id)
{
    search_data data;

    data.process_id = process_id;
    data.window_handle = 0;

    EnumWindows(enum_windows_callback, (LPARAM)&data);
    return data.best_handle;
}

#endif

void enableDebugPriv()
{
    HANDLE hToken;
    LUID luid;
    TOKEN_PRIVILEGES tkp;

    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);

    LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);

    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = luid;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    AdjustTokenPrivileges(hToken, false, &tkp, sizeof(tkp), NULL, NULL);

    CloseHandle(hToken); 
}

DWORD findProcIdByName( TCHAR* pname )
{
	int found = 0;

    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (Process32First(snapshot, &entry) == TRUE)
    {
        while (Process32Next(snapshot, &entry) == TRUE)
        {
            if ( _tcsicmp(entry.szExeFile, pname) == 0)
            {  
                HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
				if (hProc){
					CloseHandle(hProc); found = 1;
				}
				break;			
            }
        }
    }

    CloseHandle(snapshot);
	if (found){
		return entry.th32ProcessID;
	}
	return 0;
}


0

Dzięki. Zaraz się za to biorę. Co do błędu w składni to producent napisał mi, że wysyłam wiadomość nie do tego okna co trzeba. Najpierw napisali, że korzystają z FindWindow, a teraz, że w pierwszej wiadomości przekazywany jest HWND do okna. Muszę tylko teraz to zweryfikować, ponieważ nie rzuciło mi się w oczy żeby w lpData był zawarty jakiś HWND.

0

Mam takie coś w pierwszym message:

charData = 0x001ede50 L"PATHJPG=C:\\ProgramData\\X\\X\\Temp\\PHOTOSMOPATH.jpg,THICK=35.,SUBTH=0.,CAMERA=1,"

Nie widzę tu nigdzie HWND okna.

Drugie pytanie, dlaczego moja odpowiedź w podglądzie podczas debugowania wygląda tak:
screenshot-20180523114048.png

0
litrmleka napisał(a):

Mam takie coś w pierwszym message:

charData = 0x001ede50 L"PATHJPG=C:\\ProgramData\\X\\X\\Temp\\PHOTOSMOPATH.jpg,THICK=35.,SUBTH=0.,CAMERA=1,"

Nie widzę tu nigdzie HWND okna.

Drugie pytanie, dlaczego moja odpowiedź w podglądzie podczas debugowania wygląda tak:
screenshot-20180523114048.png

Proszę pisz bardziej technicznie. Skąd mam wiedzieć czy piszesz o komunikacie do serwera czy klienta przykładowo. A na tym zdjęciu coś chyba wycięte jest... To debugowanie to pod visualem pewnie ... Daj pełną rozpiskę co w tej strukturze do WM_COPYDATA jest i dalej we wskaźniku w niej zawartym. Tam jest też identyfikator - chyba pierwsze pole i jego przeznaczenie ogólnie jest do rozróżniania typów komunikatów. Ale też można to omijać.

0

Przepraszam, ja ciągle mam to na tapecie, więc wydaje mi się takie oczywiste, że mogę coś nie dopowiedzieć, w poprzednim poście napisałem:

Najpierw napisali, że korzystają z FindWindow, a teraz, że w pierwszej wiadomości przekazywany jest HWND do okna.

I to właśnie mam w pierwszej wiadomości z NC:

charData = 0x001ede50 L"PATHJPG=C:\\ProgramData\\X\\X\\Temp\\PHOTOSMOPATH.jpg,THICK=35.,SUBTH=0.,CAMERA=1,"

Nie widzę tu nigdzie hwnd, a czy może być zawarty w parametrze SendMessage - WPARAM ?

Co do screenu z visuala to chodzi mi o te chińskie krzaki, nad podglądem z debug (te krzaki) jest rzeczywista wartość stringa. I pytanie skąd te krzaki.

0

Nie czytałem wątku, ale jak już korzystasz z .NET do komunikacji między apkami to czemu nie użyć NamedPipes albo AnonymousPipes zamiast WinAPI?

0
litrmleka napisał(a):

Przepraszam, ja ciągle mam to na tapecie, więc wydaje mi się takie oczywiste, że mogę coś nie dopowiedzieć, w poprzednim poście napisałem:

Najpierw napisali, że korzystają z FindWindow, a teraz, że w pierwszej wiadomości przekazywany jest HWND do okna.

I to właśnie mam w pierwszej wiadomości z NC:

charData = 0x001ede50 L"PATHJPG=C:\\ProgramData\\X\\X\\Temp\\PHOTOSMOPATH.jpg,THICK=35.,SUBTH=0.,CAMERA=1,"

Nie widzę tu nigdzie hwnd, a czy może być zawarty w parametrze SendMessage - WPARAM ?

Co do screenu z visuala to chodzi mi o te chińskie krzaki, nad podglądem z debug (te krzaki) jest rzeczywista wartość stringa. I pytanie skąd te krzaki.

Właściwie to nawet powinni w wparam przekazać ten uchwyt. Ale to czysta odpowiedzialność użytkownika i system o to nie dba. Jeśli chodzi o interpretację wyczynów debbugera, to muszę mieć kod dookoła tego punktu pracy, najlepiej całą procedure gdzie bawisz się z messagem od serwera. Oczywiście plus definicje struktur, które przeglądałeś.

0

SendMessage (chińskie krzaki podczas debugowania):

   TCHAR* lpszString = (TCHAR*)"DIMX=4291.026,DIMY=2541.795,OFFSX=73.28035,OFFSY=67.47179,PATH=C:\\ProgramData\\X\\X\\Temp\\PHOTOSMOPATH.jpg,MMFACTOR=0.73,THICK=1.,MATERIAL=,CATEGORY=,ROTANG=0.,CORRTH=1.,LOWRES=0,HINTBOX=0,";
		COPYDATASTRUCT cds;
		cds.dwData = ID_COPYDATA_PHOTODONE;
		cds.cbData = sizeof(TCHAR) * (_tcslen(lpszString) + 1);
		cds.lpData = (TCHAR*) lpszString;

		SendMessage(hWndFromFinder, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)(LPVOID)&cds);

Odczyt:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_COPYDATA:
	{
		COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)lParam;
		
	    LPCTSTR charData = (LPCTSTR)pcds->lpData;
		
		break;
	}
	}
}

Jak odczytać to co znajduje się w WPARAM?

1
litrmleka napisał(a):

SendMessage (chińskie krzaki podczas debugowania):

   TCHAR* lpszString = (TCHAR*)"DIMX=4291.026,DIMY=2541.795,OFFSX=73.28035,OFFSY=67.47179,PATH=C:\\ProgramData\\X\\X\\Temp\\PHOTOSMOPATH.jpg,MMFACTOR=0.73,THICK=1.,MATERIAL=,CATEGORY=,ROTANG=0.,CORRTH=1.,LOWRES=0,HINTBOX=0,";
		COPYDATASTRUCT cds;
		cds.dwData = ID_COPYDATA_PHOTODONE;
		cds.cbData = sizeof(TCHAR) * (_tcslen(lpszString) + 1);
		cds.lpData = (TCHAR*) lpszString;

		SendMessage(hWndFromFinder, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)(LPVOID)&cds);

Odczyt:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_COPYDATA:
	{
		COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)lParam;
		
	    LPCTSTR charData = (LPCTSTR)pcds->lpData;
		
		break;
	}
	}
}

Jak odczytać to co znajduje się w WPARAM?

Jeśli chodzi o kwestię wParm (a nie definicji typu WPARAM) to sprawa prosta:
HWND servWnd = (HWND)wParam ;

natomiast coś takiego (TCHAR*)"mój string" to jest bezsensowna konstrukcja. TCHAR to typ definiowany w zależności czy w trybie unicode czy ansi kompilujesz. Albo jest to char albo WCHAR. Z tego wynika, że jak tworzysz apkę w trybie unicode, to przypisujesz stringowi unicode string ansi i tutaj nie ma żadnych konwersji, to jest c. Dostajesz bzdury. Na dobitek nie ma zera na końcu stringa unicode i funkcje działające na unicode mogą wyjść poza dozwolony zakres pamięci. Także debugger może doczytywać pamięć poza końcem stringa unicode, bo nie widzi jego końca.
Mam na myśli ten kawałek oczywiście - TCHAR* lpszString = (TCHAR*)"DIMX=4291.026,DIMY=2541.795 .... ";
Normalnie należy to robić tak:
TCHAR* data = _T("mój string");
i makro _T dopisuje co trzeba
Z drugiej strony nie ma sensu w kodzie do komunikacji używać TCHAR - twój serwer jest już skompilowany i używa konkretnego wariantu char albo WCHAR do komunikacj i takiego samego typu trzeba użyć.

0

Użyłem tak wParam, ale niestety to też nie jest prawidłowe hwnd, ponieważ nie dociera do NC. Zostanę przy GetProcess i MainWindowHandle, tylko pewnie przez użycie TCHARA nie rozpoznaje mi komunikatu.

A mógłbyś mi napisać jakby wyglądał ten kawałek jeśli zamiast TCHAR użyłbym CHAR lub WCHAR:

      cds.dwData = ID_COPYDATA_PHOTODONE;
      cds.cbData = sizeof(TCHAR) * (_tcslen(lpszString) + 1);
      cds.lpData = (TCHAR*) lpszString;

Bo jak próbuję CHAR lub WCHAR to mam błędy przy _tcslen.

Jak próbuję TCHAR lpszString = _T("string") to mam błąd: "a value of type "const wchar_t *" cannot be used to initialize an entity of type "TCHAR". Niestety słabo znam C++, a ta poprzednia konstrukcja to gdzieś z jakiejś dokumentacji lub forum.

1
litrmleka napisał(a):

Użyłem tak wParam, ale niestety to też nie jest prawidłowe hwnd, ponieważ nie dociera do NC. Zostanę przy GetProcess i MainWindowHandle, tylko pewnie przez użycie TCHARA nie rozpoznaje mi komunikatu.

A mógłbyś mi napisać jakby wyglądał ten kawałek jeśli zamiast TCHAR użyłbym CHAR lub WCHAR:

      cds.dwData = ID_COPYDATA_PHOTODONE;
      cds.cbData = sizeof(TCHAR) * (_tcslen(lpszString) + 1);
      cds.lpData = (TCHAR*) lpszString;

Bo jak próbuję CHAR lub WCHAR to mam błędy przy _tcslen.

Jak próbuję TCHAR lpszString = _T("string") to mam błąd: "a value of type "const wchar_t *" cannot be used to initialize an entity of type "TCHAR". Niestety słabo znam C++, a ta poprzednia konstrukcja to gdzieś z jakiejś dokumentacji lub forum.

TCHAR lpszString = _T("string") ; - przypisujesz znakowi adres i jest to bez sensu choć w kompilacji c zamiast cpp by przeszło. Powinno być: TCHAR* lpszString = _T("string") ;
Z komunikatu wnioskuję, że kompilujesz z opcją unikodu i przy tym założeniu kod powyżej oznacza, że usiłujesz wysłać serwerowi string unikodu. Wszytko jest ok poza jednym lpszString jest starym dobrym stringiem c z czasów kiedy o unkodzie nikt nie słyszał a próbujesz udawać że jest typu wchar_t*. lpszString zajmuje strlen(lpszString) bajtów, z drugiej strony _tcslen rozwija się do wcslen i traktując tablicę bajtów jak tablicę elementów wchar_t szuka zera. Najczęściej znajdzie go daleko za końcem prawdziwego stringa.
Masz coś takiego dla obu hipotetycznych wersji:


void ncsvSendMsgW( HWND clientHwnd, HWND servHwnd, DWORD cmd, const  WCHAR* cmdStr )
{
      COPYDATASTRUCT cds;

      WCHAR *cs2 = _wcsdup(cmdStr);
      if (!cs2) return;

      cds.dwData = cmd; //e.g. ID_COPYDATA_PHOTODONE;
      cds.cbData = sizeof(WCHAR) * (wcslen(cmdStr) + 1);
      cds.lpData = cs2;

      SendMessage(servHwnd, WM_COPYDATA, (WPARAM)clientHwnd, (LPARAM)&cds);
      free(cs2);
};

void ncsvSendMsgA( HWND clientHwnd, HWND servHwnd, DWORD cmd, const CHAR* cmdStr )
{
      COPYDATASTRUCT cds;
      CHAR *cs2 = _strdup(cmdStr);
      if (!cs2) return;

      cds.dwData = cmd; //e.g. ID_COPYDATA_PHOTODONE;
      cds.cbData =  (strlen(cmdStr) + 1);
      cds.lpData = cs2;

      SendMessage(servHwnd, WM_COPYDATA, (WPARAM)clientHwnd, (LPARAM)&cds);
      free(cs2);
};


void test_ansi(HWND clientHwnd, HWND servHwnd )
{
    ncsvSendMsgA( clientHwnd, servHwnd, ID_COPYDATA_PHOTODONE, "komenda");
}
void test_unicodei(HWND clientHwnd, HWND servHwnd )
{
    ncsvSendMsgW( clientHwnd, servHwnd, ID_COPYDATA_PHOTODONE, L"komenda");
}

clientHwnd to musi być okno które ma ten callback doczepiony jako procedure okienkową
servHwnd - to co tam znalazłeś polując na okno NC

Ponieważ w stringu polecenia mogą być nazwy plików, obstawiam, że serwer używa raczej unikodu. Inaczej trzeba byłoby nazwy np do utf8 konwertować przed włączeniem do polecenia.
Duplikacja do cs2 komend jest raczej tylko by kompilator był zadowolony. O ile pamiętam system nie zmienia bloku przekazywanego w cds.
Nie chciałem żebyś wchodził w meandry modyfikatora const czytając komunikaty kompilatora.

Edit:
Zastosowałem cytowanie małych wycinków kodu, bo gwiazdki się nie wyświetlają jako, że są tu meta-znakami. Dość to niefortunny wybór...

0

Może to jeszcze jakaś wskazówka.
Wysłałem pytanie do producenta o ten kod:

   TCHAR* lpszString = (TCHAR*)"DIMX=....,LOWRES=0,HINTBOX=0,";
		COPYDATASTRUCT cds;
		cds.dwData = ID_COPYDATA_PHOTODONE;
		cds.cbData = sizeof(TCHAR) * (_tcslen(lpszString) + 1);
		cds.lpData = (TCHAR*) lpszString;

To jest odpowiedź:

here's how my code is different :
cds.lpData = szString.GetBuffer() ;
and after you called ::SendMessage, you need to call
szString.ReleaseBuffer() ;
About other data, that's the same code.

1 użytkowników online, w tym zalogowanych: 0, gości: 1