Тема: Как программно отменить текущую команду?

При программном управлении рисунком AutoCAD из приложения,
через COM интерфейс, есть следующая проблема:
если пользователь выполнял в AutoCAD команду, например: Lengthen -
AutoCAD находится режиме ожидания реакции пользователя
(это видно по командной строке), а это блокирует выполнение команд приложения.
Приходится руками нажимать в AutoCAD клавишу Esc.
Как это сделать программно, т.е. программно отменить (завершить)
текущую команду.
Спасибо.

Re: Как программно отменить текущую команду?

> 127.0.0.1
https://www.caduser.ru/forum/topic26900.html
Оно?

Re: Как программно отменить текущую команду?

даже не знаю что сказать...
конечно видел советы решить эту проблему отправкой Esc в командную строку AutoCAD, наверно это бы помогло, да вот только - COM вызовы любго метода AutoCAD, в том числе и SendCommand, в момент ожидания недоступны!!!
может такое поведение зависит от версии? (пробовал на acad2002 и 2004)
может это только у меня так? - прочим спрашивающим финт с отправкой Esc помогал?

Re: Как программно отменить текущую команду?

Есть еще один вариант, послать команду не через COM. Находишь HWND активного документа AutoCAD. Дальше описываю как это делается на C/C++. На Delphi перепишешь сам:

HWND h; // HWND для активного документа
const char *cancel_str = "\x1b\x1b"; // ^C^C
COPYDATASTRUCT cmdMsg;
cmdMsg.dwData = (DWORD)1;
cmdMsg.cbData = (DWORD)strlen(cancel_str) + 1;
cmdMsg.lpData = cancel_str;
SendMessage(h, WM_COPYDATA, (WPARAM)h,(LPARAM)&cmdMsg);

Re: Как программно отменить текущую команду?

Смотри последний отредактированный вариант. Странно, но задавшему вопрос в теме, в которую я отсылал, это помогло. Увы, но на Delphi не пишу и проверить не могу. :(

Re: Как программно отменить текущую команду?

А вот это самый стабильно работающий вариант. Прерывает даже Realtime PAN/ZOOM. Маленькое замечание - должен быть запущен только один экземпляр AutoCAD.

HWND hCommandLine = NULL; // HWND дял командной строки
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
  char buf[256];
  // Находим имя окна
  GetWindowText(hwnd,buf,sizeof(buf)-1);
  // Находим коммандную строку - окно с именем HeadLands
  if (!stricmp(buf,"HeadLands")) {
    hCommandLine = hwnd; // Сохраняем ее HWND в глобальной переменной
    return false;
  }
  return true;
}
void SendCancel()
{
  HRESULT hr = NOERROR;
  CLSID   clsid;
  AutoCAD::IAcadApplicationPtr    m_iApp;
  // Поиск регистрации AutoCAD 2004...2006
  hr = ::CLSIDFromProgID(L"AutoCAD.Application.16", &clsid);
  LRESULT l = 0;
  if (SUCCEEDED(hr))
  {
    if(m_iApp.GetActiveObject(clsid) != S_OK) {
      AfxMessageBox("AutoCAD не запущен!");
      return;
    }
    HWND hwnd = (HWND)m_iApp->GetHWND(); // Находим HWND для главного окна AutoCAD
    if (!hCommandLine) {
      EnumChildWindows(hwnd,EnumWindowsProc,0);
    }
    if (hCommandLine) {
      PostMessage(hCommandLine,WM_KEYUP,VK_ESCAPE,0); // Дважды посылаем ESC
      PostMessage(hCommandLine,WM_KEYUP,VK_ESCAPE,0); // в командную строку
    }
  } else {
    AfxMessageBox("AutoCAD не зарегистрирован!");
  }
}

Надеюсь найдется кому его перевести на Delphi. :)

Re: Как программно отменить текущую команду?

Что означает эта запись ?
AutoCAD::IAcadApplicationPtr    m_iApp;
напрмиер m_iApp; ?
AutoCAD - это родитель для IAcadApplicationPtr ?

Re: Как программно отменить текущую команду?

Кстати, откуда всплыл GetActiveObject - в 2002 такого метода для AcadDocument нет ?

Re: Как программно отменить текущую команду?

Ну может вы нам все библиотеку dll с расшаренной функцией сделаете ? :))

Re: Как программно отменить текущую команду?

А на бейсике Случайно нет этой функции :)) ?

Re: Как программно отменить текущую команду?

> Alexander Larionov
1) AutoCAD::IAcadApplicationPtr - это эквивалент AcadApplication
2) GetActiveObject - это метод не AcadDocument, а AcadApplication. В VBA можно использовать GetObject(,"AutoCAD.Application.15") для AutoCAD 2000...2002, GetObject(,"AutoCAD.Application.16") для AutoCAD 2004...2006 и GetObject(,"AutoCAD.Application.17") для AutoCAD 2007...
Это если AutoCAD уже запущен. Если его нужно запустить, то соответственно используется CreateObject()

> Alexander Larionov
Нет. Мне кажется больше будет толку, если ты сам сможешь это реализовать :)

> Alexander Larionov
Нет.

Re: Как программно отменить текущую команду?

m_iApp->GetHWND();
Нет такого метода у IAcadApplication - смотрел в менеджере объектов !
Откуда это взялось ?

Re: Как программно отменить текущую команду?

> Alexander Larionov
В AutoCAD 2002 этого действительно нет. Только начиная с 2004.

Re: Как программно отменить текущую команду?

Вот переписал функцию с СИ на Паскаль.
+ Еще много чего по проверке наличия Автокада.
"Мы выстояли, мы выдержали, мы победили".
Л.И. Брежнев

procedure TForm1.Button2Click(Sender: TObject);
var
hwnd1: HWND;
WndHandle: THandle;
sp:string;
Cl:string;
i:integer;
function GetAcadHWND: HWND;
function EnumCBFun(h: HWND; parm: integer): BOOL; cdecl;
    var wt: array[0..511] of char;
  begin
    ZeroMemory(@wt, sizeof(wt));
    Result:=not (GetWindowText(h, @wt[0], ((sizeof(wt)-1) div (sizeof(char))))<>0);
    if not Result
      then Result:=not(AnsiCompareStr(wt, acCAPT)=0);
    if Result
      then acHWND:=0
      else acHWND:=h;
  end;
begin
try
//Проверка запуска АКАДА
Acad:=GetActiveOleObject('AutoCAD.Application.15') as IAcadApplication;
except
try
//При False запуск АКАДА
Acad:=CreateOleObject('AutoCAD.Application.15') as IAcadApplication;
except
ShowMessage('AutoCAD не может быть запущен !');
exit;
end;
end;
//Видимость АКАДА
////Acad.visible:=true;
AcadApp := IDispatch(Acad) as IAcadApplication;
///acCAPT:=acadApp.Caption;
EnumWindows(@EnumCBFun, 0);
Result:=acHWND;
end;
function EnumWindowsProc(hwnd2:HWND; lParam:LPARAM):boolean;stdcall;
var
buf:string[255];
spec:cardinal;
a:string;
begin
  // Находим имя окна
GetWindowText(hwnd2, @buf, sizeof(buf)-1);
a:=buf;
a:=(copy(a,1,8));
if AnsiCompareText(a, 'eadlands')=0 then begin
hCommandLine:= hwnd2; // Сохраняем ее HWND в глобальной переменной
result:=false;
exit;
end;
result:=true;
end;
function IsOLEObjectInstalled(Name: String): boolean;
var
ClassID: TCLSID;
Rez : HRESULT;
begin
// Ищем CLSID OLE-объекта
Rez := CLSIDFromProgID(PWideChar(WideString(Name)), ClassID);
if Rez = S_OK then  // Объект найден
Result := true
else
Result := false;
end;
function GetOrCreateObject(const ClassName: string): IDispatch;
var
ClassID: TGUID;
Unknown: IUnknown;
begin
try
ClassID:=ProgIDToClassID (ClassName);
if SUCCEEDED(GetActiveObject(ClassID,nil,Unknown))
then OleCheck(Unknown.QueryInterface(IDispatch,Result))
else Result:=CreateOleObject (ClassName);
except
 MessageDlg('На данном компьютере AutoCAD не установлен!!!', mtError, [mbOK], 0);
end;
end;
function CreateAutoCAD: Variant;
begin
if Not IsOLEObjectInstalled('AutoCAD.Application.15') then
begin
MessageDlg('На данном компьютере AutoCAD 2002 не установлен!!!', mtError, [mbOK],0);
Result := Null;
end;
Result := GetOrCreateObject('AutoCAD.Application.15');
end;
begin
if (IsOLEObjectInstalled('AutoCAD.Application.15')=true) then
CreateAutoCAD else
begin
ShowMessage('Не найден AutoCAD');
exit;
end;
hwnd1:=0;
try
Acad:=GetActiveOleObject('AutoCAD.Application.15') as IAcadApplication;
except
//При False запуск АКАДА
Acad:=CreateOleObject('AutoCAD.Application.15') as IAcadApplication;
end;
//Видимость АКАДА
////Acad.visible:=true;
AcadApp := IDispatch(Acad) as IAcadApplication;
HWND1:=FindWindow(nil, 'AutoCAD 2002');
i:=1;
if (HWND1=0) then begin
while (HWND1=0) and (i<300) do
begin
HWND1:=FindWindow(nil, pansichar('AutoCAD 2002 - [Drawing'+inttostr(i)+'.dwg]'));
i:=i+1;
end;
end;
if (HWND1=0) and (i=300) then begin
ShowMessage('Чертеж не найден !');
exit;
end;
if HWND1<>0 then begin
{GetClassName(HWND1, Cs, 255);
ShowMessage(inttostr(HWND1));
ShowMessage(Cs);
HWND1:=FindWindow(Cs, nil);
ShowMessage(inttostr(HWND1)); }
end;
if HWND1=0 then
begin
ShowMessage('AutoCAD не обнаружен !');
exit;
end;
EnumChildWindows(hwnd1,@EnumWindowsProc,0);
if hCommandLine=0 then begin
EnumChildWindows(hwnd1,@EnumWindowsProc,0);
end;
if hCommandLine<>0 then begin
PostMessage(hCommandLine,WM_KEYUP,VK_ESCAPE,0); // Дважды посылаем ESC
PostMessage(hCommandLine,WM_KEYUP,VK_ESCAPE,0); // в командную строку
///Acad.visible:=true;
end
else begin
ShowMessage('AutoCAD не зарегистрирован!');
end;
end;

Re: Как программно отменить текущую команду?

На супер оптимальность не претендую :))

Re: Как программно отменить текущую команду?

Вот и отлично. Надеюсь, что работает. :)

Re: Как программно отменить текущую команду?

AutoCAD::IAcadApplicationPtr — это эквивалент AcadApplication

У меня компилятор ругается, что не знает пространства имен AutoCAD. Как еще средствами ObjectARX можно получить хэндл окна автокада в которое загружена моя длл-ка ?

Re: Как программно отменить текущую команду?

Упс, ошибся форумом )))

Re: Как программно отменить текущую команду?

А "из нутри" АКАДа сие можно проделывать? Скажем из Лиспа вызвать СOM-север и уже из него послать Esc в ком. строку? Первые эксперименты показали что нельзя. Имеет ли смысл ковырять дальше?

Re: Как программно отменить текущую команду?

Теоретически должно работать.

Re: Как программно отменить текущую команду?

Теоритечески да, практически не работает.
Не находится подчиненное окно с заголовком "HeadLands" (ни через Application, ни через процесс). Если сравнивать хэндлы, то по "нужному" обнаруживается окно с заголовком "Command: ", посылка которому PostMessage ни к чему не приводит.

Re: Как программно отменить текущую команду?

> bender
Этот код был сделан из расчета на AutoCAD 2000...2002 и 2004...2006. Для последних трех версий не проверял.

Re: Как программно отменить текущую команду?

> Александр Ривилис
Да там не в версии похоже дело. Если выполнить все это "из вне" (из какого-нибудь exe-шника), то все находится и посылается. Единственное я HWND главного окна AutoCAD нахожу не через CLSIDFromProgID, а через процесс или через GetObject.