PDA

View Full Version : مشکل در ایجاد یک thread برای ارسال خروجی به اکسل



mortezakiaee
شنبه 04 اسفند 1386, 10:56 صبح
یه thread برای فرستادن اطلاعات به اکسل اجاد کردم اما وقتیexcute میشه پیام خطای زیرو میده

The Application called an interaface that marshalled for a differnt thread

کد thread:

var WorkBk : _WorkBook; WorkSheet : _WorkSheet; I : OleVariant; st : Variant;
R,k,j,l:Integer; c:char;
temp_qry:string;
begin
I := 1;
Form1.ExcelApplication1.Connect;
WorkBk := Form1.ExcelApplication1.Workbooks.Add(I,0);
WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
WorkSheet.Activate(0);
Form1.ExcelApplication1.Visible[i]:=true;
R:= 1;
c:='A';



Form1.ADOTable1.First;
For l :=0 to Form1.ADOTable1.RecordCount-1 do begin
temp_qry:=' select * from sorat_tbl where ghararwithpey_code = '+QuotedStr(Form1.ADOTable1GHAR_WPEY.AsString);
Form1.ADOQuery1.Close;
Form1.ADOQuery1.SQL.Clear;
Form1.ADOQuery1.SQL.Add(temp_qry);
Form1.ADOQuery1.Open;



if Form1.ADOQuery1.RecordCount = 0 then
//ExcelApplication1.Range['b'+IntToStr(R),'b'+IntToStr(R)].Value2:='ÕæÑÊ æÖÚíÊí ÈÑÇí Çíä ÔãÇÑå ÞÑÇÑÏÇÏ æÌæÏ äÏÇÑÏ'
else begin
inc(R);
Form1.ExcelApplication1.Range['A'+IntToStr(R),'A'+IntToStr(R)].Value2:= 'ÞÑÇÑÏÇÏ ÔãÇÑå:';
Form1.ExcelApplication1.Range['b'+IntToStr(R),'b'+IntToStr(R)].Value2:= Form1.ADOTable1GHAR_WPEY.AsVariant;
inc(R);
for j:=0 to Form1.ADOQuery1.RecordCount-1 do begin
c:='b';

for k:=1 to Form1.ADOQuery1.FieldCount-1 do begin
inc(c);
Form1.ExcelApplication1.Range[C+inttostr(R),C+inttostr(R)].Value2:=Form1.ADOQuery1.Fields[k].AsVariant;end;
inc(R);
Form1.ADOQuery1.Next; end;
end;

Form1.ADOTable1.Next;
end;

Form1.ExcelApplication1.Range['a1:'+c+IntToStr (R),emptyparam].Font.Name:='B Nazanin';
Form1.ExcelApplication1.Range['a1:'+c+IntToStr (R),emptyparam].Font.Size:=12;
Form1.ExcelApplication1.Range['a1:'+c+IntToStr (R),emptyparam].Borders.Weight :=xlThin;
Form1.ExcelApplication1.Range['f1','h1:'+c+'1'].EntireColumn.NumberFormat:='#,##0';
WorkSheet.DisplayRightToLeft[0]:=true;
WorkSheet.Columns.AutoFit;
Beep;
Form1.ExcelApplication1.Disconnect;

vcldeveloper
شنبه 04 اسفند 1386, 11:50 صبح
وقتی از تکنولوژی های مرتبط با ActiveX (مثلا ADO) در یک Thread استفاده میکنید، باید قبلش در اون Thread با CoInitialize اونها را مقداردهی اولیه کنید. در Thread اصلی برنامه این کار بصورت خودکار توسط دلفی انجام میشه، اما در سایر Thread ها خودتون باید این کار را بکنید. آیا این کار را کردید؟ البته تا جایی که یادم هست در صورت مقداردهی اولیه نکردن، در خود متن پیام خطا مستقیما به CoInitialize اشاره میشد، پس ممکنه این راه حل مشکل شما رو حل نکنه. در هر حال، اگه همین واژه CoInitialize را جستجو کنید، قبلا درباره نحوه انجام آن بحث شده.

mortezakiaee
شنبه 04 اسفند 1386, 12:24 عصر
این کارو نکردم ولی خط مولد خطا این خطه:
WorkBk := Form1.ExcelApplication1.Workbooks.Add(I,0);

mortezakiaee
شنبه 04 اسفند 1386, 12:52 عصر
کسی یه نمونه از ارسال اطلاعات به اکسل با thread نداره؟

mortezakiaee
شنبه 04 اسفند 1386, 13:05 عصر
خطاشو با گذاشتن این کد ها در یه تابع private و syncronize اون توی متدexecute حل شد. اما عملا تغییری توی کار برنامه ایجاد نشد. هنوز هم وقتی داره exportمی کنه برنامه به حالت هنگ در میاد. تعریف thread من اشتباهه یا به همین شکله؟

unit EXTE_thread_unt;

interface

uses
Classes, excelxp, hjj1, strutils, variants;

type
EXTE_thread = class(TThread)
private
procedure exte;
{ Private declarations }
protected
procedure Execute; override;
end;

implementation

uses SysUtils;

{ Important: Methods and properties of objects in visual components can only be
used in a method called using Synchronize, for example,

Synchronize(UpdateCaption);

and UpdateCaption could look like,

procedure EXTE_thread.UpdateCaption;
begin
Form1.Caption := 'Updated in a thread';
end; }

{ EXTE_thread }

procedure EXTE_thread.exte;
var WorkBk : _WorkBook; WorkSheet : _WorkSheet; I : OleVariant; st : Variant;
R,k,j,l:Integer; c:char;
temp_qry:string;
begin
I := 1;
Form1.ExcelApplication1.Connect;
WorkBk :=Form1.ExcelApplication1.Workbooks.Add(I,0);
WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
WorkSheet.Activate(0);

R:= 1;
c:='A';


{for j:=0 to HIGH(Titlelists) do begin
Inc(C);
St := titlelists[j];
TExcelApplication(ServName).Range[c+inttostr(T_I),c+inttostr(T_I)].Value2:=St;end;}

Form1.ADOTable1.First;
For l :=0 to Form1.ADOTable1.RecordCount-1 do begin
temp_qry:=' select * from sorat_tbl where ghararwithpey_code = '+QuotedStr(Form1.ADOTable1GHAR_WPEY.AsString);
Form1.ADOQuery1.Close;
Form1.ADOQuery1.SQL.Clear;
Form1.ADOQuery1.SQL.Add(temp_qry);
Form1.ADOQuery1.Open;



if Form1.ADOQuery1.RecordCount = 0 then
//ExcelApplication1.Range['b'+IntToStr(R),'b'+IntToStr(R)].Value2:='ÕæÑÊ æÖÚíÊí ÈÑÇí Çíä ÔãÇÑå ÞÑÇÑÏÇÏ æÌæÏ äÏÇÑÏ'
else begin
inc(R);
Form1.ExcelApplication1.Range['A'+IntToStr(R),'A'+IntToStr(R)].Value2:= 'ÞÑÇÑÏÇÏ ÔãÇÑå:';
Form1.ExcelApplication1.Range['b'+IntToStr(R),'b'+IntToStr(R)].Value2:= Form1.ADOTable1GHAR_WPEY.AsVariant;
inc(R);
for j:=0 to Form1.ADOQuery1.RecordCount-1 do begin
c:='b';

for k:=1 to Form1.ADOQuery1.FieldCount-1 do begin
inc(c);
Form1.ExcelApplication1.Range[C+inttostr(R),C+inttostr(R)].Value2:=Form1.ADOQuery1.Fields[k].AsVariant;end;
inc(R);
Form1.ADOQuery1.Next; end;
end;

Form1.ADOTable1.Next;
end;

Form1.ExcelApplication1.Range['a1:'+c+IntToStr (R),emptyparam].Font.Name:='B Nazanin';
Form1.ExcelApplication1.Range['a1:'+c+IntToStr (R),emptyparam].Font.Size:=12;
Form1.ExcelApplication1.Range['a1:'+c+IntToStr (R),emptyparam].Borders.Weight :=xlThin;
Form1.ExcelApplication1.Range['f1','h1:'+c+'1'].EntireColumn.NumberFormat:='#,##0';
WorkSheet.DisplayRightToLeft[0]:=true;
WorkSheet.Columns.AutoFit;
Form1.ExcelApplication1.Visible[i]:=true;
Beep;

Form1.ExcelApplication1.Disconnect;
end;

procedure EXTE_thread.Execute;
begin
Synchronize(exte);
end;

end.

vcldeveloper
شنبه 04 اسفند 1386, 17:19 عصر
این کارو نکردم
پس حتما بکنید.


خطاشو با گذاشتن این کد ها در یه تابع private و syncronize اون توی متدexecute حل شد.
چیزی حل نشد. متد Synchronize کد مربوطه رو در داخل Thread اصلی برنامه اجرا میکنه و فقط برای آپدیت های ساده رابط کاربر مفید هست. با این کاری که شما انجام دادید، عملا فرقی بین اجرای کد در داخل یک Thread مستقل و اجرای کل آن در داخل Thread اصلی برنامه وجود نداره.

mortezakiaee
شنبه 04 اسفند 1386, 17:28 عصر
مشکل پیام خطا با این عمل حل شد. اما مشکل جدید اینه که عملا تو نحوه اجرای برنامه تغییری حاصل نشد. یعنی هنوزم تو مدت ارسال اطلاعات برنامه فریز میشه و نمیشه هیچ کار دیگه ای مثل کنسل کردن رویداد رو انجام داد.
فکر نمی کنم مربوط به coinitialize باشه. هست؟

Mahmood_M
یک شنبه 05 اسفند 1386, 23:56 عصر
مشکل پیام خطا با این عمل حل شد. اما مشکل جدید اینه که عملا تو نحوه اجرای برنامه تغییری حاصل نشد. یعنی هنوزم تو مدت ارسال اطلاعات برنامه فریز میشه و نمیشه هیچ کار دیگه ای مثل کنسل کردن رویداد رو انجام داد.
فکر نمی کنم مربوط به coinitialize باشه. هست؟
کدهای مربوط به دستور exte رو به قسمتهای کوچکتری تبدیل کنید و برای فراخوانی هر قسمت در داخل متد Execute مربوط به Thread ، از Synchronize استفاده کنید ...
تابع Synchronize کار همزمانسازی چند رویداد رو انجام میده ، وقتی شما فقط یک رویداد رو در Thread با Synchronize انجام میدید ، مشخصه که فرقی با حالت عادی نمی کنه و همه کدها مثل حالت عادی پشت سر هم اجرا میشن ...

بهتره که ساختار Thread کمی منظم تر بشه و کدها به قطعات کوچکتر تبدیل بشن ...
مثلا میتونید اجرای دستورات SQL رو در یک متد جدا Synchronize کنید و یا دستور Add کردن در ExcelApplication رو ...

موفق باشید ...

jpsprogrammer
دوشنبه 06 اسفند 1386, 01:08 صبح
من ارسال رو انجام دادم اما با یه ترفندی که خودم لازم داشتم اگه بخوائی بایستی یه سری تغییر رو سورس بدم تا قابل استفاده برای شما هم باشه اگه خواستی ایمیل بزن jpsprogrammer@gmail.com

vcldeveloper
دوشنبه 06 اسفند 1386, 01:40 صبح
مشکل پیام خطا با این عمل حل شد. اما مشکل جدید اینه که عملا تو نحوه اجرای برنامه تغییری حاصل نشد. یعنی هنوزم تو مدت ارسال اطلاعات برنامه فریز میشه و نمیشه هیچ کار دیگه ای مثل کنسل کردن رویداد رو انجام داد.در پست قبل توضیح دادم که Synchronize کد را در Context ثرید اصلی اجرا میکنه، یعنی اگه از Synchronize استفاده کنید مثل این هست که اصلا Multi-threading استفاده نکرده باشید. Synchronize ثرید شما رو stop میکنه، کدی که بهش دادید رو در Thread اصلی اجرا میکنه، بعد دوباره اجرای Thread شما رو ادامه میده، یعنی فرایند موازی شما تبدیل میشه به یک فرایند سریال. علت برطرف شدن خطا هم همینه. گفتم که برای استفاده از ActiveX در هر Thread باید در شروع اون Thread از CoInitialize استفاده بشه. وقتی کد مربوط به کار با ADO و Excel را در داخل Synchronize استفاده می کنید، چون کد در داخل Thread اصلی اجرا میشه، نیازی هم به CoInitialize نیست و خطایی هم ایجاد نمیشه، چون کد بصورت یک کد Single-Threaded عادی اجرا میشه. خب، مشخص هست که در این حالت در زمان انجام کار رابط کاربرتون (در واقع Thread اصلی برنامه) حالت قفل شده پیدا میکنه.
اگر می خواید کارها بصورت موازی (نه سریال) انجام بشه، باید اون کدها را از Synchronize بیرون بیارید و توی Execute استفاده کنید. این کار باعث میشه که بطور عادی خطا بگیرید. برای برطرف کردن خطا باید قبل از استفاده از ActiveX آن را با CoInitialize مقداردهی اولیه کنید که شرحش قبلا در تاپیک های قدیمی تر اومده.
مفهوم شد؟


تابع Synchronize کار همزمانسازی چند رویداد رو انجام میده ، وقتی شما فقط یک رویداد رو در Thread با Synchronize انجام میدید ، مشخصه که فرقی با حالت عادی نمی کنه و همه کدها مثل حالت عادی پشت سر هم اجرا میشن ...
اینطور نیست. Synchronize همون کاری رو انجام میده که در بالا گفتم. ربطی به اینکه چند کار در داخل Thread انجام میشه، نداره.

mortezakiaee
دوشنبه 06 اسفند 1386, 10:34 صبح
با تشکر از توضیحات خوبتون آقای کشاورز. جستجو در برای کلمه CoInitialize نتیجه مفیدی نداشت. با یه سری جستجوی دیگه به این مقاله رسیدم که مفید بود
http://delphi.about.com/od/kbthread/a/query_threading.htm?p=1
ولی نتیجه این که ایراد اصلی از CoInitialize نیست. همونطور که گفتم خط مربوط به ایجاد یه ورک بوک جدید تو اکسل خطا میده که ربطی به ado نداره

بازم قضیه به قوت خودش باقیه. آیا نمی شه از oleserver ها تو ترید استفاده کرد؟ به نظر ابلهانه میاد:افسرده:

mortezakiaee
دوشنبه 06 اسفند 1386, 11:25 صبح
کسی یه مرجع جامع در مورد OLE تو دلفی نداره؟:عصبانی++::گیج:

vcldeveloper
دوشنبه 06 اسفند 1386, 11:50 صبح
CoInitialize فقط مربوط به ADO نیست. Excel هم شامل میشه. گفتم هر چیزی که از ActiveX استفاده کنه.


کسی یه مرجع جامع در مورد OLE تو دلفی نداره؟
توی کتاب های مختلفی مثل Mastering Delphi توضیح داده شده. سایت های مختلفی هم روی این موضوع کار کردند. دقت کنید که چیزی که شما نیاز دارید دونستن یکسری کلیات OLE و یکسری توابع برای کار با اون هست، بعد از اون باید روی متدها و خصوصیاتی که کلاس Excel ارائه میکنه مسلط بشید. برای مطالعه در اون زمینه هم می تونید به MSDN مراجعه کنید. یک راه خوب اینه که کاری رو که می خواید انجام بدید رو توی خود Excel یک بار امتحان کنید و ازش یک Macro بگیرید، بعد کد Macro ایجاد شده رو بررسی کنید تا بفهمید Excel چطور اون کار رو انجام میده. اون وقت می تونید خودتون همون کار رو با استفاده از OLE انجام بدید.

mortezakiaee
دوشنبه 06 اسفند 1386, 13:35 عصر
بلاخره مشکل رو فهمیدم:



بهتره که ساختار Thread کمی منظم تر بشه و کدها به قطعات کوچکتر تبدیل بشن ...


صد درصد موافقم. فقط دستوراتی که پروسه گیر هستن باید داخل excute باشن


در پست قبل توضیح دادم که Synchronize کد را در Context ثرید اصلی اجرا میکنه، یعنی اگه از Synchronize استفاده کنید مثل این هست که اصلا Multi-threading استفاده نکرده باشید. Synchronize ثرید شما رو stop میکنه، کدی که بهش دادید رو در Thread اصلی اجرا میکنه، بعد دوباره اجرای Thread شما رو ادامه میده، یعنی فرایند موازی شما تبدیل میشه به یک فرایند سریال.


علت برطرف شدن خطا هم همینه. گفتم که برای استفاده از ActiveX در هر Thread باید در شروع اون Thread از CoInitialize استفاده بشه.



CoInitialize فقط مربوط به ADO نیست. Excel هم شامل میشه. گفتم هر چیزی که از ActiveX استفاده کنه.

گوشه هایی از حقیقت:چشمک:. کاملش اینه که شما نمی تنید متد های ole رو توی متد excute یه ترید اجرا کنید چون خودشون یه ترید جدا گانه هستن.
خطا: مارشال شدن همزمان چند ترید



نکته مهم اینه که ado ها نمی تونن از کانکشن اصلی برنامه استفاده کنن اطلاعات تکمیلیش توی همون لینکی تو چند تا پست قبلی آوردم هست.


به هر حال از همه ممنون بدون همفکریتون نمی تونستم راه حل درستو پیدا کنم

vcldeveloper
دوشنبه 06 اسفند 1386, 15:04 عصر
گوشه هایی از حقیقت:چشمک:. کاملش اینه که شما نمی تنید متد های ole رو توی متد excute یه ترید اجرا کنید چون خودشون یه ترید جدا گانه هستن.خیر...
از MSDN مبحث OLE Controls and Control Containers Guidelines


Starting with Microsoft Windows® 95 and Windows NT™ version 3.51, OLE provides
support for multithreading applications, allowing applications to make OLE calls
from multiple threads. This multithreaded support is called the "apartment model."
The apartment model requires that interface pointers be marshalled (using CoMarshallInterface,
and CoUnmarshallInterface) when passed between threads. For more information
about apartment-model threading, refer to the Microsoft Win32® Software
Development Kit (SDK) documentation, and the OLEAPT sample in the Win32 SDK.

Mahmood_M
دوشنبه 06 اسفند 1386, 23:27 عصر
تابع Synchronize کار همزمانسازی چند رویداد رو انجام میده ، وقتی شما فقط یک رویداد رو در Thread با Synchronize انجام میدید ، مشخصه که فرقی با حالت عادی نمی کنه و همه کدها مثل حالت عادی پشت سر هم اجرا میشن ...
ببخشید که این سئوال رو در این تاپیک می پرسم ...
پس کار Synchronize دقیقا چیه ؟!
من از Help دلفی این برداشت رو کردم که این متد باعث میشه که Thread ساخته شده متوقف ( Suspend ) بمونه تا برنامه ، متد مورد نظر ( متدی که به Synchronize پاس میشه ) رو در Thread اصلی برنامه اجرا کنه ...
درسته ؟!

...

vcldeveloper
سه شنبه 07 اسفند 1386, 06:45 صبح
من از Help دلفی این برداشت رو کردم که این متد باعث میشه که Thread ساخته شده متوقف ( Suspend ) بمونه تا برنامه ، متد مورد نظر ( متدی که به Synchronize پاس میشه ) رو در Thread اصلی برنامه اجرا کنه ...
درسته ؟!
بله این درسته، ولی برداشت قبلی شما اشتباه بود.

mortezakiaee
سه شنبه 07 اسفند 1386, 07:53 صبح
خیر...
از MSDN مبحث OLE Controls and Control Containers Guidelines
.....


حالا که مساله حل شد. ولی بهتر نبود از اول به جای coinitilaze در مورد
CoMarshallInterface یه راهنمایی می کردین؟:چشمک: