Нужна помощь. Ошибка при использовании DDE из DLL-ки на С

 
Здравствуйте, уважаемые!

В настоящий момент занимаюсь написанием некоей приблуды для МТ4. Для обновления котировки инструмента использую горячее соединение DDE.
После посылки серверу сообщения WM_DDE_UNADVISE, МТ4 в журнале выдает следующее сообщение: DDE: stop advise failed for '' (в конце именно две одинарные кавычки)

WM_DDE_UNADVISE посылаю следующим образом:
PostMessage(g_hwndServer, WM_DDE_UNADVISE, (WPARAM)hwndClient, MAKELPARAM(CF_TEXT, NULL));
пробовал так:
PostMessage(g_hwndServer, WM_DDE_UNADVISE, (WPARAM)hwndClient, PackDDElParam(WM_DDE_UNADVISE, (UINT_PTR)CF_TEXT, (UINT_PTR)NULL));
результат тот же.

Привожу фрагмент кода оконной процедуры окна-клиента (весь код не влез и пришлось убрать из него инициализацию горячего DDE-соединения и обработку сообщения WM_DDE_DATA)

void
DDE_Terminate(HWND hwndClient)
{
  PostMessage(g_hwndServer, WM_DDE_UNADVISE, (WPARAM)hwndClient, PackDDElParam(WM_DDE_UNADVISE, (UINT_PTR)CF_TEXT, (UINT_PTR)NULL));
  PostMessage(g_hwndServer, WM_DDE_TERMINATE, (WPARAM)hwndClient, 0L);
  g_hwndServer = NULL;
 
  return;
}
 
LRESULT CALLBACK
Proc_Main(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
  static BOOL   bInitMode = TRUE;
  ATOM          atomTerminal, atomTopic, atomItem;
  DDEACK        DdeAck;
  DDEDATA      *pDdeData;
  DDEADVISE    *pDdeAdvise;
  GLOBALHANDLE  hDdeAdvise, hDdeData;
  UINT          uiLow, uiHi;
  WORD          wStatus;
  double        open;
  char          szItem[MAX_BUF];
  char          szBid[MAX_BUF];
  char         *stoped;
 
  switch (uiMsg)
  {
  case WM_DDE_ACK:
    if (bInitMode)
    {
      UnpackDDElParam(WM_DDE_ACK, lParam, &uiLow, &uiHi);
      FreeDDElParam(WM_DDE_ACK, lParam);
      g_hwndServer = (HWND)wParam;
      GlobalDeleteAtom((ATOM)uiLow);
      GlobalDeleteAtom((ATOM)uiHi);
    }
    return(0);
 
  case WM_DDE_TERMINATE:
    PostMessage(g_hwndServer, WM_DDE_TERMINATE, (WPARAM)hWnd, 0L);
    g_hwndServer = NULL;
    return(0);
 
  case WM_CLOSE :
    if (g_hwndServer == NULL)
      break;
    DDE_Terminate(hWnd);
    break;
 
  case WM_DESTROY:
    if (g_Symbol)
      zmem_Free(g_Symbol);
    PostQuitMessage(0);
    return(0);
 
  }
  return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}

ЗЫ: опыт разработки программ на чистом WinAPI - довольно богатый, но с DDE работаю впервые, так что сильно по почкам не бейте :)
Писал на основе примера Петцольда
 
Уф... видать никого данная тема не интересует.
Тогда конкретный вопрос разработчикам. Скорее всего, даже Renat'у, так как, насколько я понял, реализация DDE в МТ4 - его рук дело.

В каких случаях МТ4 выдает указанную ошибку?
Если не сочтете это наглостью, то можете просто привести кусок кода из обработчика исключений, так чтоб мне было понятно, в каком месте мне руки рашпилем подточить.

С уважением,
Дмитрий
 
Не знаю как там на C, но на Delphi это прекрасно работает, притом использую это эдак с версии 180-й....
Вот пример работающего класса, который берет котировки.
Важно: работает в активном режиме, т.е. запускать метод RequestMTBidAsk нужно из-под таймера.
Вызывающая сторона хранит предыдущие котировки и заботится о том, чтобы распознать именение котировок.

type
TprBA = record
Bid: Currency;
Ask: Currency;
end;

{ TprMTAsker - Запросы в МТ на котировки }
TprMTAsker = class(TPersistent)
private
FNoDataCounter: Integer;
FOnDataModeChanged: TNotifyEvent;
protected
DdeClientConv: TDdeClientConv;
DdeClientItem: TDdeClientItem;
public
constructor Create(const AOwner: TComponent); virtual;
destructor Destroy; override;
function RequestMTBidAsk(const ASymbolName: string; var ABA: TprBA): Boolean;
property OnDataModeChanged: TNotifyEvent read FOnDataModeChanged write FOnDataModeChanged;
property NoDataCounter: Integer read FNoDataCounter;
end;

{ TprMTAsker }
constructor TprMTAsker.Create(const AOwner: TComponent);
begin
inherited Create;
FNoDataCounter := 0;
DdeClientConv := TDdeClientConv.Create(nil);
DdeClientConv.ConnectMode := ddeAutomatic;
DdeClientItem := TDdeClientItem.Create(nil);
DdeClientItem.DdeConv := DdeClientConv;
end;

destructor TprMTAsker.Destroy;
begin
if Assigned(DdeClientConv) then
DdeClientConv.CloseLink;
if Assigned(DdeClientItem) then
begin
DdeClientItem.DdeConv := nil;
FreeAndNil(DdeClientItem);
end;
if Assigned(DdeClientConv) then FreeAndNil(DdeClientConv);
inherited Destroy;
end;

{ ASymbolName - строковое значение символа (например 'EURUSD')
ABA - запись с котировкой, уходящая вверх в вызывающий метод.
Внутри ведется счетчик неудачных попыток получить котировки, при превышении им
некоторого порога запускается эвент }
function TprMTAsker.RequestMTBidAsk(const ASymbolName: string; var ABA: TprBA): Boolean;
var
FStr, FBid, FAsk: string;
FPos, FPriorNoDataCounter: Integer;
begin
Result := False;
try
try
if (DdeClientConv.ConnectMode <> ddeAutomatic) then
DdeClientConv.ConnectMode := ddeAutomatic;
DdeClientConv.SetLink('MT4', 'QUOTE');
DdeClientItem.DdeItem := ASymbolName;
FStr := DdeClientItem.Text;
DdeClientConv.CloseLink;
if (FStr <> '') then
begin
FPos := Pos(':', FStr);
if (FPos > 0) then
begin
FStr := Trim(DelStartStr2(FStr, FPos + 3));
FBid := Trim(ExtractStrUntilChar(FStr, ' '));
FAsk := Trim(DelStartStr(FStr, FBid + ' '));
if IsValidStringForDouble(FBid) and IsValidStringForDouble(FAsk) then
begin
ABA.Bid := StrToFloat(FBid);
ABA.Ask := StrToFloat(FAsk);
Result := True;
FPriorNoDataCounter := FNoDataCounter;
FNoDataCounter := 0;
if (FPriorNoDataCounter <> 0) then
begin
if Assigned(OnDataModeChanged) then
OnDataModeChanged(Self);
end;
end;
end;
end
else
begin
FNoDataCounter := FNoDataCounter + 1;
if (FNoDataCounter > 100) then
FNoDataCounter := 100;
if (FNoDataCounter > 10) then
begin
if Assigned(OnDataModeChanged) then
OnDataModeChanged(Self);
end;
end;
finally
DdeClientConv.CloseLink;
end;
except
end;
end;
{ TprMTAsker }

--
IsValidStringForDouble, DelStartStr2, ExtractStrUntilChar, DelStartStr - некоторые кустомные функции работы со строками, заменить потом на свои.
 
FZfin:
Не знаю как там на C, но на Delphi это прекрасно работает, притом использую это эдак с версии 180-й....
Вот пример работающего класса, который берет котировки.
Спасибо, камрад, за помощь, но твой выстрел, мягко говоря, не в ту сторону.
Тем более, что вот это:
FZfin:
Важно: работает в активном режиме, т.е. запускать метод RequestMTBidAsk нужно из-под таймера.
Вызывающая сторона хранит предыдущие котировки и заботится о том, чтобы распознать именение котировок.
ИМХО, не есть правильно.


ЗЫ: с проблемой разобрался сам. Всем спасибо.