Eli tilanne on tämä: Minulla on DLL-kirjasto, joka on tehty C++:lla. Lisäksi minulla on ohjelma, joka on tehty C#:lla (editori). Yritän tehdä sellaista väkerrystä, että kun editorista painetaan nappia, kutsutaan DLL:stä funktiota, joka käynnistää taustasäikeen (tarkoituksena on taustaprosessi, jotta editoria voidaan käyttää prosessin aikana). Taustasäikeen tehtävänä on parsia tekstitiedostoja.
Haluan kuitenkin, että säie ja ohjelma voivat komminikoida keskenään (esim. virhetilanteet säikeessä ilmoitetaan ohjelmalle). Kun esimerkiksi virhetilanne tapahtuu (tiedoston luvuissa havaitaan virhe), lähettää säie SendMessage (tai vastaava) ohjelmalle tiedon siitä.
Seuraavaksi vaiheet selitettyinä yksi kerrallaan ja koodin kanssa.
1) Kun käyttäjä painaa nappia, kutsutaan dll:stä funktiota:
[DllImport("myLib.dll", EntryPoint = "StartThread", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)] public static extern void StartThread( [MarshalAs(UnmanagedType.LPStr)] string filename, IntPtr hwnd ); /////////// private void MyButton_Click(object sender, EventArgs e) { ((Button)sender).Enabled = false; // vain yksi taustaprosessi kerrallaan StartThread( "myfile.txt", this.Handle ); // annetaan kahva kommunikointia varten }
2) DLL käynnistää säikeen:
// otos dll:n koodista (c++) // viestin id #define __MSG_EXIT__ 0xFF321F2 struct Settings { std::string filename; HWND host_hwnd; // the handle received from application }; /** globaalit asetukset! */ Settings* gSettings = 0; /** tämä on itse säiefunktio */ DWORD WINAPI __internel_ThreadFunc(LPVOID lpParam) { while(1) { // koodia.... } // LÄHETETÄÄN VIESTI OHJELMALLE SÄIKEEN LOPPUMISESTA!! TÄRKEÄ!! SendMessage( gSettings->host_hwnd, __MSG_EXIT__, NULL, NULL ); // testausta varten ::MessageBoxA( NULL, "SUCCESS!", "", MB_OK ); return 0; } extern "C" __declspec(dllexport) void StartThread(const char* fname, HWND hwnd) { gSettings = new Settings(); gSettings->filename = fname; gSettings->host_hwnd = hwnd // luodaan ja käynnistetään säie HANDLE thread = CreateThread( NULL, 0, __internel_ThreadFunc, 0, 0, NULL); if( thread == NULL ) { ExitProcess( 0 ); ::MessageBoxA( NULL, "FAIL!", "", MB_OK ); } }
3) Tällä tavalla ohjelma käsittelee viestejä:
public partial class MainForm : Form { // ..... koodia ..... const int __MSG_EXIT__ = 0xFF321F2; [System.Security.Permissions.PermissionSet( System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")] protected override void WndProc(ref Message m) { switch (m.Msg) { // tarkistetaan lopetusviesti case __MSG_EXIT__: // ... käsitellään viesti ... break; } base.WndProc(ref m); } }
Noh... Ongelma on sitten tämä: DLL ei lähetä tuota __MSG_EXIT__ viestiä tai ohjelma ei ota sitä vastaan. Olen kuitenkin 100% varma, että säie saavuttaa lähetyskohdan, sillä alla oleva Messuboksi ilmestyy ruudulle ("SUCCESS!"). Silti viestiä ei ohjelmaan ilmaannu.
Olenko tehnyt jotain karmivasti pieleen? Miten ongelman saisi korjattua?
Kiitos etukäteen.
Joo-o. Ratkaistu.
Viesti piti rekisteröidä RegisterWindowMessage:lla ennen käyttöä.
Aihe on jo aika vanha, joten et voi enää vastata siihen.