Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > postmessage в отсутствии окна


Автор: gonzales 16.11.2007, 17:29
Доброе время суток, уважаемые форумчане!!!
столкнулся с одной задачей, в силу своего ламерства не могу придумать как ее решить.
Суть следующая:
имеется юнит работы с ком-портом, который содержит следующую функцию
Код

//функция дочернего потока, обрабатывающего эвенты порта
//параметр введен для того, чтобы каждый (их стартует два)
//экземпляр работал с переменными "своего" порта
function WorkComm(var Parms : TComVar) : longint; stdcall;
var
  Kols,Mask,Trans,Errs : DWord;
  Ovr : TOverlapped;
  Buff : array [0..511] of byte;
  Modems, LenPacket : DWord;
  Reciv : pointer;
begin
  Result := 0;
//инициализируем TOverlapped структуру
  FillChar(Ovr,SizeOf(TOverlapped),0);
  Ovr.hEvent := CreateEvent(nil,TRUE,FALSE,#0);
//стартуем бесконечный цикл
  while not Parms.Terminated do with Parms do begin
    if Terminated then break;
    Mask := 0;
//запрашиваем (ожидаем) маску эвентов
    if not WaitCommEvent(cId,Mask,@Ovr) then begin
    //обрабатываем "отложенную" операцию ввода-вывода
      if (GetLastError() = ERROR_IO_PENDING) and
         (WaitForSingleObject(Ovr.hEvent,INFINITE)=Wait_Object_0)
        then GetOverlappedResult(cId,Ovr,Trans,False);
    end;
//очищаем ошибки с получением статуса порта, обязательно!!!
    ClearCommError(cId,Errs,@Stat);
    if Terminated then break;
    if Mask = 0  then continue;
    if (Mask and EV_RXCHAR) = EV_RXCHAR then begin
    //если принят хотя бы один байт
        if Stat.cbInQue < 512
         then Kols := Stat.cbInQue
       else Kols := 512;
      if Terminated then break;
    //пытаемся читать принятые байты
      ReadFile(cId,Buff,Kols,Kols,@Ovr);
      if Terminated then break;
      if Kols > 0 then begin
      //если хоть один байт считан - переносим в буфер накопления
        Move(Buff,RecivBuff[CntByte],Kols);
        inc(CntByte,Kols);

          while CntByte >= 1 do begin
//        while CntByte >= sizeof(TPacketHeader) do begin
        //пока количество принятых байт больше длины синхрочасти преамбулы
        //пытаемся засинхронизоваться
//          if Cardinal((@RecivBuff[0])^) = Sync
         if RecivBuff[0] = Sync
           then begin
             if CntByte >= kols
              then begin
              //если синхропосылка стоит вначале накопленного буфера
              //и общая длина принятых байт больше или равна длине
              //преамбулы плюс количество байт, указанное в преамбуле
                LenPacket := kols;

                 //если этот экземпляр потока работает с СОМ1
                 //получаем указатель, переписываем туда все байты пакета
                   GetMem(Reciv,LenPacket);
                   Move(RecivBuff,Reciv^,LenPacket);
                 //шлем сообщение основному потоку
                   PostMessage(fmMain.Handle,cmRxByte,Integer(Reciv),LenPacket);

                //удаляем из накопленного буфера байты обработанного выше пакета
                dec(CntByte,LenPacket);
                Move(RecivBuff[LenPacket],RecivBuff[0],CntByte);
              end
              //в случае неполного принятия пакета (синхропосылка вначале буфера)
              else break;
           end
           else begin
             //в случае, если начало накопленного буфера не содержит
             //синхропосылку, удаляем первый байт со сдвигом всего буфера
             dec(CntByte);
             Move(RecivBuff[1],RecivBuff[0],CntByte);
           end;
    //    end;
        end;
      end;
    end;
    if ((Mask and EV_DSR) = EV_DSR) or
       ((Mask and EV_CTS) = EV_CTS) then begin
      //если случилось изменение модемных линий
        if Terminated then break;
      if Master then begin
        //если этот экземпляр потока работает с СОМ1
        //запрашиваем текущее состояние модемных линий
        GetCommModemStatus(cId,Modems);
        //получаем указатель
        GetMem(Reciv,sizeof(TModems));
        //заполняем указатеь
        TModems(Reciv^).DSR := (Modems and MS_DSR_ON) = MS_DSR_ON;
        TModems(Reciv^).CTS := (Modems and MS_CTS_ON) = MS_CTS_ON;
        //шлем сообщение основному потоку
        PostMessage(fmMain.Handle,cmModem,Integer(Reciv),0);
      end;
    end;
  end;
  CloseHandle(Ovr.hEvent);
end;


как видно из описания в начале функции это функция дочернего потока, обрабатывающего эвенты порта, которая передает данные основному потоку через
Код

PostMessage(fmMain.Handle,cmRxByte,Integer(Reciv),LenPacket);
  

и вроде все правильно и все работает. Но тут мне захотелось все это удовольствие запихнуть в длл, соответственно формы fmMain, которой передается сообщение нет!!!
и тут загвоздка, как передать в основной поток (если я правильно выражаюсь) данные????


Автор: Lexicss 16.11.2007, 17:38
Могу, конечно, и ошибаться, но по-ходу никак. 
Сообщения ведь могут принимать либо окна, либо потоки. 
Раз нет окна, попробуй созадать поток-обработчик.

Автор: gonzales 16.11.2007, 17:41
ну ведь можно же наверное послать сообщение типа всем всем всемsmile))

Автор: Lexicss 16.11.2007, 17:53
Можно. smile
Но примут их только те объекты у которых есть Handle, ИМХО.

Автор: ama_kid 16.11.2007, 23:44
Цитата(gonzales @  16.11.2007,  17:29 Найти цитируемый пост)
как передать в основной поток (если я правильно выражаюсь) данные?
Основной поток будет содержать окно? Вот и вызывай после LoadLibrary (но перед началом основной работы) функцию инициализации с передачей в dll хендла главного окна и потом пуляй уже в него сообщениями сколько влезет... Хотя имхо правильнее структура, когда приложение, использующее библиотеку, само забирает необходимые данные...

Автор: MetalFan 17.11.2007, 00:16
callback функция?

Автор: dumb 17.11.2007, 16:59
Код

var
  hWndMain: HWND;
...
  hWndMain := FindWindow("TfrmMain", "caption главной формы");

Автор: gonzales 19.11.2007, 10:55
Цитата

Основной поток будет содержать окно?

если бы основной поток содержал окно, то вопросов бы не было.

Цитата

callback функция?


а можно подробнее

Автор: ama_kid 19.11.2007, 12:16
Цитата(gonzales @  19.11.2007,  10:55 Найти цитируемый пост)
если бы основной поток содержал окно, то вопросов бы не было.
Из изначального поста следует как раз, что окно существует  smile (основное окно юзающего dll приложения, т.к. ты просто захотел функциональность работы с COM-портом переместить в dll)...

Цитата(gonzales @  19.11.2007,  10:55 Найти цитируемый пост)
Цитата(MetalFan @  17.11.2007,  00:16 Найти цитируемый пост)
callback функция? 
а можно подробнее 
Утрированно: callback-функция - это функция, вызов которой осуществляется неявно... в твоем случае - это некая функция, адрес которой ты можешь передать в dll, и уже в библиотеке её вызывать, тем самым заставляя выполняться функцию основного потока из нутра dll... В качестве примера:
Код
library Project2;

uses
  SysUtils,
  Classes;

type
 TCallBackFunc = procedure (Buf:ShortString);    stdcall;
var
 MyVar:integer = 0;
 CallBackFunc:TCallBackFunc = nil;
{$R *.res}

procedure SetCallBack(P:TCallBackFunc); stdcall;
begin
 CallBackFunc:=P;
end;

procedure ExportedFunc; stdcall;
begin
 Inc(MyVar);
 if Assigned(CallBackFunc) then
  CallBackFunc(IntToStr(MyVar));
end;

exports
 SetCallBack,
 ExportedFunc;
begin
end.

Код
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
const
 LibName = 'Project2.dll';
type
 TSetCallBack = procedure (P:Pointer); stdcall;
 TDllFunc = procedure;    stdcall;


  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  h:THandle;
  hSetCallBack:Pointer;
  hExportedFunc:Pointer;

implementation

{$R *.dfm}
procedure MyCallBackFunction(Buf:ShortString); stdcall;  // Вызываемая из Dll callback-функция
begin
 ShowMessage(Buf);
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
 h:=LoadLibrary(LibName);
 if h=0 then
  begin
   MessageBox(0,'Failed to Load','!!!',0);
   exit;
  end;
 hSetCallBack:=GetProcAddress(h,'SetCallBack');
 if (hSetCallBack<>nil) then
   TSetCallBack(hSetCallBack)(@MyCallBackFunction);    // Здесь передаем в библиотеку адрес callback-функции
 hExportedFunc:=GetProcAddress(h,'ExportedFunc');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 if (hExportedFunc<>nil) then
   TDllFunc(hExportedFunc);   // вызываем библиотечную функцию, которая в свою очередь вызовет нашу callback-функцию MyCallBackFunction;
end;
end.

Автор: gonzales 19.11.2007, 13:29
спасибо за пример, буду разбираться. Идею я уловил. 

PS. Я ничего не передаю в основную форму, которая конечно же есть. Библиотека - это плагин, от которого в основное приложение идут только функции, заложенные в основное приложение. А дочерний и первичный поток идут в самой длл, просто в разных юнитах

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)